下面列出了怎么用android.view.Choreographer的API类实例代码及写法,或者点击链接到github查看源代码。
@Override
public void doFrame(long frameTimeNanos) {
long elapsedNanos = frameTimeNanos - lastFrameTimeNanos;
double fps = TimeUnit.SECONDS.toNanos(1) / (double) elapsedNanos;
if (elapsedNanos > badFrameThresholdNanos) {
if (consecutiveFrameWarnings < MAX_CONSECUTIVE_FRAME_LOGS) {
long droppedFrames = elapsedNanos / idealTimePerFrameNanos;
Log.w(TAG, String.format(Locale.ENGLISH, "Bad frame! Took %d ms (%d dropped frames, or %.2f FPS)", TimeUnit.NANOSECONDS.toMillis(elapsedNanos), droppedFrames, fps));
consecutiveFrameWarnings++;
}
} else {
consecutiveFrameWarnings = 0;
}
fpsData.add(fps);
runningAverageFps.add(fps);
lastFrameTimeNanos = frameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
}
@Override
public void doFrame(long frameTimeNanos) {
double averageFps = 0;
int size = fpsData.size();
for (double fps : fpsData) {
averageFps += fps / size;
}
if (averageFps < badIntervalThresholdFps) {
if (consecutiveIntervalWarnings < MAX_CONSECUTIVE_INTERVAL_LOGS) {
Log.w(TAG, String.format(Locale.ENGLISH, "Bad interval! Average of %.2f FPS over the last %d ms", averageFps, TimeUnit.NANOSECONDS.toMillis(frameTimeNanos - lastReportTimeNanos)));
consecutiveIntervalWarnings++;
}
} else {
consecutiveIntervalWarnings = 0;
}
lastReportTimeNanos = frameTimeNanos;
updateRefreshRate();
Choreographer.getInstance().postFrameCallbackDelayed(this, REPORTING_INTERVAL);
}
@Override
public void run() {
setName("ChorRenderThread");
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
Log.d(TAG, "got message, quitting");
Looper.myLooper().quit();
}
};
Choreographer.getInstance().postFrameCallback(this);
Looper.loop();
Log.d(TAG, "looper quit");
Choreographer.getInstance().removeFrameCallback(this);
}
@Override
public void onCreate() {
super.onCreate();
mContext = this;
Tips.init(this);
DataCenter.init(this, "zhuanlan.db");
// StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
// .detectDiskReads()
// .detectDiskWrites()
// .detectNetwork()
// .penaltyLog()
// .build());
//
// StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
// .detectActivityLeaks()
// .detectLeakedSqlLiteObjects()
// .penaltyLog()
// .penaltyDeath()
// .build());
initStetho();
Choreographer choreographer = Choreographer.getInstance();
choreographer.postFrameCallback(FRAME_CALLBACK);
}
@Override
public void doFrame(long frameTimeNanos) {
// Log.d(TAG, "frameTimeNanos = " + frameTimeNanos);
if (lastFrameTimeNanos == 0) {
lastFrameTimeNanos = frameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
return;
}
currentFrameTimeNanos = frameTimeNanos;
long value = (currentFrameTimeNanos - lastFrameTimeNanos) / 1000000;
// 大于16ms表示发生卡顿
if (value > 16) {
Log.d(TAG, "currentFrameTimeNanos - lastFrameTimeNanos = " + (currentFrameTimeNanos - lastFrameTimeNanos) / 1000000);
}
lastFrameTimeNanos = currentFrameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void getFPS() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return;
}
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (mStartFrameTime == 0) {
mStartFrameTime = frameTimeNanos;
}
long interval = frameTimeNanos - mStartFrameTime;
if (interval > MONITOR_INTERVAL_NANOS) {
double fps = (((double) (mFrameCount * 1000L * 1000L)) / interval) * MAX_INTERVAL;
Log.d(TAG, String.valueOf(fps));
mFrameCount = 0;
mStartFrameTime = 0;
} else {
++mFrameCount;
}
Choreographer.getInstance().postFrameCallback(this);
}
});
}
@Override
public void doFrame(long frameTimeNanos) {
if (!enabled) {
destroy();
return;
}
if (startSampleTimeInNs == 0) {
startSampleTimeInNs = frameTimeNanos;
}
//是否完成一个周期
boolean aCycleComplete = frameTimeNanos - startSampleTimeInNs > fpsConfig.getSampleTimeInNs();
if (aCycleComplete) {
if (fpsConfig.frameDataCallback != null) {
fpsConfig.frameDataCallback.getFPS(dataSet);
}
collectSampleAndSend(frameTimeNanos);
}
dataSet.add(frameTimeNanos);
Choreographer.getInstance().postFrameCallback(this);
}
/**
* GLコンテキスト上で実行されるChoreographer.FrameCallbackをpostする
* @param callback
* @param delayMs
* @throws IllegalStateException
*/
public synchronized void postFrameCallbackDelayed(
@NonNull final Choreographer.FrameCallback callback,
final long delayMs) throws IllegalStateException {
if (DEBUG) Log.v(TAG, "postFrameCallbackDelayed:");
checkValid();
if (isGLThread()) {
// すでにGLスレッド上であれば直接実行
Choreographer.getInstance().postFrameCallbackDelayed(callback, delayMs);
} else {
// 別スレッド上にいるならGLスレッド上へ投げる
mGLHandler.post(new Runnable() {
@Override
public void run() {
Choreographer.getInstance().postFrameCallbackDelayed(callback, delayMs);
}
});
}
}
/**
* 未実行のChoreographer.FrameCallbackがあれば取り除く
* @param callback
* @throws IllegalStateException
*/
public synchronized void removeFrameCallback(
@NonNull final Choreographer.FrameCallback callback)
throws IllegalStateException {
if (DEBUG) Log.v(TAG, "removeFrameCallback:");
checkValid();
if (isGLThread()) {
// すでにGLスレッド上であれば直接実行
Choreographer.getInstance().removeFrameCallback(callback);
} else {
// 別スレッド上にいるならGLスレッド上へ投げる
mGLHandler.post(new Runnable() {
@Override
public void run() {
Choreographer.getInstance().removeFrameCallback(callback);
}
});
}
}
/**
* ワーカースレッド終了時の処理(ここはまだワーカースレッド上)
*/
@WorkerThread
protected final void handleOnStop() {
if (DEBUG) Log.v(TAG, "onStop");
if ((mEnableVSync) && (mChoreographerHandler != null)) {
mChoreographerHandler.post(new Runnable() {
@Override
public void run() {
Choreographer.getInstance().removeFrameCallback(mFrameCallback);
}
});
}
notifyParent(false);
makeCurrent();
internalOnStop();
handleRemoveAll();
// if (DEBUG) Log.v(TAG, "onStop:finished");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated holder=" + holder);
File outputFile = new File(getFilesDir(), "fbo-gl-recording.mp4");
SurfaceView sv = (SurfaceView) findViewById(R.id.fboActivity_surfaceView);
mRenderThread = new RenderThread(sv.getHolder(), new ActivityHandler(this), outputFile,
MiscUtils.getDisplayRefreshNsec(this));
mRenderThread.setName("RecordFBO GL render");
mRenderThread.start();
mRenderThread.waitUntilReady();
mRenderThread.setRecordMethod(mSelectedRecordMethod);
RenderHandler rh = mRenderThread.getHandler();
if (rh != null) {
rh.sendSurfaceCreated();
}
// start the draw events
Choreographer.getInstance().postFrameCallback(this);
}
@Override
protected void onResume() {
super.onResume();
// If we already have a Surface, we just need to resume the frame notifications.
if (mRenderThread != null) {
Log.d(TAG, "onResume re-hooking choreographer");
Choreographer.getInstance().postFrameCallback(this);
}
updateControls();
}
private void scheduleColorFadeDraw() {
if (!mColorFadeDrawPending) {
mColorFadeDrawPending = true;
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
mColorFadeDrawRunnable, null);
}
}
@Override
public void doFrame(long frameTimeNanos) {
RenderHandler rh = mRenderThread.getHandler();
if (rh != null) {
Choreographer.getInstance().postFrameCallback(this);
rh.sendDoFrame(frameTimeNanos);
}
}
BoundsAnimationController(Context context, AppTransition transition, Handler handler,
AnimationHandler animationHandler) {
mHandler = handler;
mAppTransition = transition;
mAppTransition.registerListenerLocked(mAppTransitionNotifier);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mAnimationHandler = animationHandler;
if (animationHandler != null) {
// If an animation handler is provided, then ensure that it runs on the sf vsync tick
handler.runWithScissors(() -> mChoreographer = Choreographer.getSfInstance(),
0 /* timeout */);
animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
}
}
@Override
public void doFrame(long frameTimeNanos)
{
//if not enabled then we bail out now and don't register the callback
if (!enabled){
destroy();
return;
}
//initial case
if (startSampleTimeInNs == 0){
startSampleTimeInNs = frameTimeNanos;
}
// only invoked for callbacks....
else if (fpsConfig.frameDataCallback != null)
{
long start = dataSet.get(dataSet.size()-1);
int droppedCount = Calculation.droppedCount(start, frameTimeNanos, fpsConfig.deviceRefreshRateInMs);
fpsConfig.frameDataCallback.doFrame(start, frameTimeNanos, droppedCount,currentFPS);
}
//we have exceeded the sample length ~700ms worth of data...we should push results and save current
//frame time in new list
if (isFinishedWithSample(frameTimeNanos))
{
collectSampleAndSend(frameTimeNanos);
}
// add current frame time to our list
dataSet.add(frameTimeNanos);
//we need to register for the next frame callback
Choreographer.getInstance().postFrameCallback(this);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void startMonitorFrameInfo() {
DokitMemoryConfig.FPS_STATUS = true;
//开启定时任务
mMainHandler.postDelayed(mRateRunnable, FPS_SAMPLING_TIME);
Choreographer.getInstance().postFrameCallback(mRateRunnable);
}
/**
* show fps meter, this regisers the frame callback that
* collects the fps info and pushes it to the ui
* @param context
*/
public void show(Context context) {
if (overlayPermRequest(context)) {
//once permission is granted then you must call show() again
return;
}
//are we running? if so, call tinyCoach.show() and return
if (tinyCoach != null) {
tinyCoach.show();
return;
}
// set device's frame rate info into the config
setFrameRate(context);
// create the presenter that updates the view
tinyCoach = new TinyCoach((Application) context.getApplicationContext(), fpsConfig);
// create our choreographer callback and register it
fpsFrameCallback = new FPSFrameCallback(fpsConfig, tinyCoach);
Choreographer.getInstance().postFrameCallback(fpsFrameCallback);
//set activity background/foreground listener
Foreground.init((Application) context.getApplicationContext()).addListener(foregroundListener);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed holder=" + holder);
// We need to wait for the render thread to shut down before continuing because we
// don't want the Surface to disappear out from under it mid-render. The frame
// notifications will have been stopped back in onPause(), but there might have
// been one in progress.
//
// TODO: the RenderThread doesn't currently wait for the encoder / muxer to stop,
// so we can't use this as an indication that the .mp4 file is complete.
RenderHandler rh = mRenderThread.getHandler();
if (rh != null) {
rh.sendShutdown();
try {
mRenderThread.join();
} catch (InterruptedException ie) {
// not expected
throw new RuntimeException("join was interrupted", ie);
}
}
mRenderThread = null;
mRecordingEnabled = false;
// If the callback was posted, remove it. Without this, we could get one more
// call on doFrame().
Choreographer.getInstance().removeFrameCallback(this);
Log.d(TAG, "surfaceDestroyed complete");
}
@TargetApi(16)
private void updateInChoreographer() {
if (quitFlag) {
return;
}
Choreographer.getInstance().postFrameCallback(mFrameCallback);
long startMS = DimensionTimer.getInstance().get2dTime();
long d = syncTimer(startMS);
if (d < 0) {
removeMessages(UPDATE);
return;
}
d = mDanmakuView.drawDanmakus();
removeMessages(UPDATE);
if (d > mCordonTime2) { // this situation may be cuased by ui-thread waiting of DanmakuView, so we sync-timer at once
timer.add(d);
mDrawTimes.clear();
}
if (!mDanmakusVisible) {
waitRendering(INDEFINITE_TIME);
return;
} else if (mRenderingState.nothingRendered && mIdleSleep) {
long dTime = mRenderingState.endTime - timer.currMillisecond;
if (dTime > 500) {
waitRendering(dTime - 10);
return;
}
}
}
public ChoreographerAndroidSpringLooper(Choreographer choreographer) {
mChoreographer = choreographer;
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (!mStarted || mSpringSystem == null) {
return;
}
long currentTime = SystemClock.uptimeMillis();
mSpringSystem.loop(currentTime - mLastTime);
mLastTime = currentTime;
mChoreographer.postFrameCallback(mFrameCallback);
}
};
}
@Override
public void doFrame(long frameTimeNanos) {
mFpsCount++;
mFrameTimeNanos = frameTimeNanos;
if (isCanWork()) {
//注册下一帧回调
Choreographer.getInstance().postFrameCallback(this);
} else {
mCurrentCount = 0;
}
}
public ChoreographerAndroidSpringLooper(Choreographer choreographer) {
mChoreographer = choreographer;
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (!mStarted || mSpringSystem == null) {
return;
}
mSpringSystem.loop();
mChoreographer.postFrameCallback(mFrameCallback);
}
};
}
@Override
public void doFrame(long l) {
invalidateSelf();
if (isLoading) {
Choreographer.getInstance().postFrameCallback(this);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void start() {
Log.d(TAG, "start vsync detect");
if (mRunning) {
return;
}
mRunning = true;
syncCheckThread = new Thread(new Runnable() {
@Override
public void run() {
for (;;) {
if (!mRunning) {
break;
}
syncCheckThread();
}
}
});
syncCheckThread.start();
Choreographer chor = Choreographer.getInstance();
Field field;
try {
field = chor.getClass().getDeclaredField("mFrameIntervalNanos");
field.setAccessible(true);
mFrameIntervalNanos = field.getLong(chor);
Log.d(TAG, "mFrameIntervalNanos " + mFrameIntervalNanos);
} catch (Exception e) {
Log.e(TAG, "error: " + e.getMessage());
}
chor.postFrameCallback(frameCallback);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Choreographer.FrameCallback getFrameCallback() {
if (mFrameCallback == null) {
mFrameCallback =
new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
ChoreographerCompat.FrameCallback.this.doFrameInternal(frameTimeNanos);
}
};
}
return mFrameCallback;
}
@Override
protected void onPause() {
super.onPause();
// TODO: we might want to stop recording here. As it is, we continue "recording",
// which is pretty boring since we're not outputting any frames (test this
// by blanking the screen with the power button).
// If the callback was posted, remove it. This stops the notifications. Ideally we
// would send a message to the thread letting it know, so when it wakes up it can
// reset its notion of when the previous Choreographer event arrived.
Log.d(TAG, "onPause unhooking choreographer");
Choreographer.getInstance().removeFrameCallback(this);
}
public ChoreographerAndroidSpringLooper(Choreographer choreographer) {
mChoreographer = choreographer;
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (!mStarted || mSpringSystem == null) {
return;
}
long currentTime = SystemClock.uptimeMillis();
mSpringSystem.loop(currentTime - mLastTime);
mLastTime = currentTime;
mChoreographer.postFrameCallback(mFrameCallback);
}
};
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed holder=" + holder);
// We need to wait for the render thread to shut down before continuing because we
// don't want the Surface to disappear out from under it mid-render. The frame
// notifications will have been stopped back in onPause(), but there might have
// been one in progress.
//
// TODO: the RenderThread doesn't currently wait for the encoder / muxer to stop,
// so we can't use this as an indication that the .mp4 file is complete.
RenderHandler rh = mRenderThread.getHandler();
if (rh != null) {
rh.sendShutdown();
try {
mRenderThread.join();
} catch (InterruptedException ie) {
// not expected
throw new RuntimeException("join was interrupted", ie);
}
}
mRenderThread = null;
mRecordingEnabled = false;
// If the callback was posted, remove it. Without this, we could get one more
// call on doFrame().
Choreographer.getInstance().removeFrameCallback(this);
Log.d(TAG, "surfaceDestroyed complete");
}
public ChoreographerAndroidSpringLooper(Choreographer choreographer) {
mChoreographer = choreographer;
mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (!mStarted || mSpringSystem == null) {
return;
}
long currentTime = SystemClock.uptimeMillis();
mSpringSystem.loop(currentTime - mLastTime);
mLastTime = currentTime;
mChoreographer.postFrameCallback(mFrameCallback);
}
};
}