本文实例讲述了Android测量每秒帧数Frames Per Second (FPS)的方法。分享给大家供大家参考。具体如下:
MainThread.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
package net.obviam.droidz;
import java.text.DecimalFormat;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
/**
* @author impaler
*
* The Main thread which contains the game loop. The thread must have access to
* the surface view and holder to trigger events every game tick.
*/
public class MainThread extends Thread {
private static final String TAG = MainThread. class .getSimpleName();
// desired fps
private final static int MAX_FPS = 50 ;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5 ;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
// Stuff for stats */
private DecimalFormat df = new DecimalFormat( "0.##" ); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000 ; //ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10 ;
// last time the status was stored
private long lastStatusStore = 0 ;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0 ;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0 ;
// the average FPS since the game started
private double averageFps = 0.0 ;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private MainGamePanel gamePanel;
// flag to hold game state
private boolean running;
public void setRunning( boolean running) {
this .running = running;
}
public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
super ();
this .surfaceHolder = surfaceHolder;
this .gamePanel = gamePanel;
}
@Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop" );
// initialise timing elements for stat gathering
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0 ;
while (running) {
canvas = null ;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this .surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0 ; // resetting the frames skipped
// update game state
this .gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this .gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = ( int )(FRAME_PERIOD - timeDiff);
if (sleepTime > 0 ) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this .gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
if (framesSkipped > 0 ) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null ) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
/**
* The statistics - it is called every cycle, it checks if time since last
* store is greater than the statistics gathering period (1 sec) and if so
* it calculates the FPS for the last period and stores it.
*
* It tracks the number of frames per period. The number of frames since
* the start of the period are summed up and the calculation takes part
* only if the next period and the frame count is reset to 0.
*/
private void storeStats() {
frameCountPerStatCycle++;
totalFrameCount++;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = ( double )(frameCountPerStatCycle / (STAT_INTERVAL / 1000 ));
//stores the latest fps in the array
fpsStore[( int ) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0 ;
// sum up the stored fps values
for ( int i = 0 ; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0 ;
statusIntervalTimer = 0 ;
frameCountPerStatCycle = 0 ;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
gamePanel.setAvgFps( "FPS: " + df.format(averageFps));
}
}
private void initTimingElements() {
// initialise timing elements
fpsStore = new double [FPS_HISTORY_NR];
for ( int i = 0 ; i < FPS_HISTORY_NR; i++) {
fpsStore[i] = 0.0 ;
}
Log.d(TAG + ".initTimingElements()" , "Timing elements for stats initialised" );
}
}
|
希望本文所述对大家的Android程序设计有所帮助。