Android系统Choreographer机制实现过程

来源:互联网 发布:2016年人工智能龙头股 编辑:程序博客网 时间:2024/04/29 14:34

原文地址如下,

http://www.360doc.com/content/15/0701/19/10366845_481984948.shtml


在Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,实现统一调度界面绘图.

Choreographer构造过程

frameworks\base\core\java\android\view\Choreographer.java

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static Choreographer getInstance() {  
  2.     return sThreadInstance.get();  
  3. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private static final ThreadLocal<Choreographer> sThreadInstance =  
  2.         new ThreadLocal<Choreographer>() {  
  3.     @Override  
  4.     protected Choreographer initialValue() {  
  5.         Looper looper = Looper.myLooper();  
  6.         if (looper == null) {  
  7.             throw new IllegalStateException("The current thread must have a looper!");  
  8.         }  
  9.         return new Choreographer(looper);  
  10.     }  
  11. };  

为调用线程创建一个Choreographer实例,调用线程必须具备消息循环功能,因为ViewRootImpl对象的构造是在应用程序进程的UI主线程中执行的,因此创建的Choreographer对象将使用UI线程消息队列。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private Choreographer(Looper looper) {  
  2.     mLooper = looper;  
  3.     //创建消息处理Handler  
  4.     mHandler = new FrameHandler(looper);  
  5.     //如果系统使用了Vsync机制,则注册一个FrameDisplayEventReceiver接收器  
  6.     mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;  
  7.     mLastFrameTimeNanos = Long.MIN_VALUE;  
  8.     //屏幕刷新周期  
  9.     mFrameIntervalNanos = (long)(1000000000 /  
  10.             new Display(Display.DEFAULT_DISPLAY, null).getRefreshRate());  
  11.     //创建回调数组  
  12.     mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];  
  13.     //初始化数组  
  14.     for (int i = 0; i <= CALLBACK_LAST; i++) {  
  15.         mCallbackQueues[i] = new CallbackQueue();  
  16.     }  
  17. }  

变量USE_VSYNC用于表示系统是否是用了Vsync同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的。如果系统使用了Vsync同步机制,则创建一个FrameDisplayEventReceiver对象用于请求并接收Vsync事件,最后Choreographer创建了一个大小为3的CallbackQueue队列数组,用于保存不同类型的Callback。

添加回调过程

frameworks\base\core\java\android\view\Choreographer.java

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void postCallback(int callbackType, Runnable action, Object token) {  
  2.     postCallbackDelayed(callbackType, action, token, 0);  
  3. }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void postCallbackDelayed(int callbackType,  
  2.         Runnable action, Object token, long delayMillis) {  
  3.     if (action == null) {  
  4.         throw new IllegalArgumentException("action must not be null");  
  5.     }  
  6.     if (callbackType < 0 || callbackType > CALLBACK_LAST) {  
  7.         throw new IllegalArgumentException("callbackType is invalid");  
  8.     }  
  9.     postCallbackDelayedInternal(callbackType, action, token, delayMillis);  
  10. }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void postCallbackDelayedInternal(int callbackType,  
  2.         Object action, Object token, long delayMillis) {  
  3.     synchronized (mLock) {  
  4.         final long now = SystemClock.uptimeMillis();  
  5.         final long dueTime = now + delayMillis;  
  6.         //将要执行的回调封装成CallbackRecord对象,保存到mCallbackQueues数组中  
  7.         mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);  
  8.         //函数执行时间到  
  9.         if (dueTime <= now) {  
  10.             scheduleFrameLocked(now);  
  11.         } else {//通过异步消息方式实现函数延时执行  
  12.             Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);  
  13.             msg.arg1 = callbackType;  
  14.             msg.setAsynchronous(true);  
  15.             mHandler.sendMessageAtTime(msg, dueTime);  
  16.         }  
  17.     }  
  18. }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private final class FrameHandler extends Handler {  
  2.     @Override  
  3.     public void handleMessage(Message msg) {  
  4.         switch (msg.what) {  
  5.             case MSG_DO_SCHEDULE_CALLBACK:  
  6.                 doScheduleCallback(msg.arg1);  
  7.                 break;  
  8.         }  
  9.     }  
  10. }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void doScheduleCallback(int callbackType) {  
  2.     synchronized (mLock) {  
  3.         if (!mFrameScheduled) {  
  4.             final long now = SystemClock.uptimeMillis();  
  5.             if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {  
  6.                 scheduleFrameLocked(now);  
  7.             }  
  8.         }  
  9.     }  
  10. }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void scheduleFrameLocked(long now) {  
  2.     if (!mFrameScheduled) {  
  3.         mFrameScheduled = true;  
  4.         //检查是否使用了Vsync机制  
  5.         if (USE_VSYNC) {  
  6.             //如果当前线程具备消息循环,则直接请求VSync信号  
  7.             if (isRunningOnLooperThreadLocked()) {  
  8.                 scheduleVsyncLocked();  
  9.             } else {//如果当前线程不具备消息循环,则通过主线程请求VSync信号  
  10.                 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);  
  11.                 msg.setAsynchronous(true);  
  12.                 mHandler.sendMessageAtFrontOfQueue(msg);  
  13.             }  
  14.         } else { //如果系统没有使用VSync机制,则使用异步消息延时执行屏幕刷新  
  15.             final long nextFrameTime = Math.max(  
  16.                     mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);  
  17.             Message msg = mHandler.obtainMessage(MSG_DO_FRAME);  
  18.             msg.setAsynchronous(true);  
  19.             mHandler.sendMessageAtTime(msg, nextFrameTime);  
  20.         }  
  21.     }  
  22. }  

在该函数中考虑了两种情况,一种是系统没有使用Vsync机制,在这种情况下,首先根据屏幕刷新频率计算下一次刷新时间,通过异步消息方式延时执行doFrame()函数实现屏幕刷新。如果系统使用了Vsync机制,并且当前线程具备消息循环,则直接请求Vsync信号,否则就通过主线程来请求Vsync信号。FrameDisplayEventReceiver对象用于请求并接收Vsync信号,当Vsync信号到来时,系统会自动调用其onVsync()函数,在该回调函数中执行doFrame()实现屏幕刷新。

当VSYNC信号到达时,Choreographer doFrame()函数被调用

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void doFrame(long frameTimeNanos, int frame) {  
  2.     final long startNanos;  
  3.     synchronized (mLock) {  
  4.         if (!mFrameScheduled) {  
  5.             return// no work to do  
  6.         }  
  7.         //保存起始时间  
  8.         startNanos = System.nanoTime();  
  9.         //由于Vsync事件处理采用的是异步方式,因此这里计算消息发送与函数调用开始之间所花费的时间  
  10.         final long jitterNanos = startNanos - frameTimeNanos;  
  11.         //如果线程处理该消息的时间超过了屏幕刷新周期  
  12.         if (jitterNanos >= mFrameIntervalNanos) {  
  13.             //计算函数调用期间所错过的帧数  
  14.             final long skippedFrames = jitterNanos / mFrameIntervalNanos;  
  15.             if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {  
  16.                 Log.i(TAG, "Skipped " + skippedFrames + " frames!  "  
  17.                         + "The application may be doing too much work on its main thread.");  
  18.             }  
  19.             final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;  
  20.             frameTimeNanos = startNanos - lastFrameOffset;  
  21.         }  
  22.         //如果frameTimeNanos小于一个屏幕刷新周期,则重新请求VSync信号  
  23.         if (frameTimeNanos < mLastFrameTimeNanos) {  
  24.             scheduleVsyncLocked();  
  25.             return;  
  26.         }  
  27.         mFrameScheduled = false;  
  28.         mLastFrameTimeNanos = frameTimeNanos;  
  29.     }  
  30.     //分别回调CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL事件  
  31.     doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);  
  32.     doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);  
  33.     doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);  
  34. }  


Choreographer类中分别定义了CallbackRecord、CallbackQueue内部类,CallbackQueue是一个按时间先后顺序保存CallbackRecord的单向循环链表。

在Choreographer中定义了三个CallbackQueue队列,用数组mCallbackQueues表示,用于分别保存CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL这三种类型的Callback,当调用Choreographer类的postCallback()函数时,就是往指定类型的CallbackQueue队列中通过addCallbackLocked()函数添加一个CallbackRecord项:首先构造一个CallbackRecord对象,然后按时间先后顺序插入到CallbackQueue链表中。从代码注释中,我们可以知道CALLBACK_INPUT是指输入回调,该回调优先级最高,首先得到执行,而CALLBACK_TRAVERSAL是指处理布局和绘图的回调,只有在所有异步消息都执行完后才得到执行,CALLBACK_ANIMATION是指动画回调,比CALLBACK_TRAVERSAL优先执行,从doFrame()函数中的doCallbacks调用就能印证这点。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);  
  2. doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);  
  3. doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);  

当Vsync事件到来时,顺序执行CALLBACK_INPUT、CALLBACK_ANIMATION和CALLBACK_TRAVERSAL对应CallbackQueue队列中注册的回调。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void doCallbacks(int callbackType, long frameTimeNanos) {  
  2.     CallbackRecord callbacks;  
  3.     synchronized (mLock) {  
  4.         final long now = SystemClock.uptimeMillis();  
  5.         //从指定类型的CallbackQueue队列中查找执行时间到的CallbackRecord  
  6.         callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);  
  7.         if (callbacks == null) {  
  8.             return;  
  9.         }  
  10.         mCallbacksRunning = true;  
  11.     }  
  12.     try {  
  13.         //由于CallbackQueues是按时间先后顺序排序的,因此遍历执行所有时间到的CallbackRecord  
  14.         for (CallbackRecord c = callbacks; c != null; c = c.next) {  
  15.             c.run(frameTimeNanos);  
  16.         }  
  17.     } finally {  
  18.         synchronized (mLock) {  
  19.             mCallbacksRunning = false;  
  20.             do {  
  21.                 final CallbackRecord next = callbacks.next;  
  22.                 recycleCallbackLocked(callbacks);  
  23.                 callbacks = next;  
  24.             } while (callbacks != null);  
  25.         }  
  26.     }  
  27. }  

该函数就是按时间顺序先后执行到时的CallbackRecord

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private static final class CallbackRecord {  
  2.     public CallbackRecord next;  
  3.     public long dueTime;  
  4.     public Object action; // Runnable or FrameCallback  
  5.     public Object token;  
  6.   
  7.     public void run(long frameTimeNanos) {  
  8.         if (token == FRAME_CALLBACK_TOKEN) {  
  9.             ((FrameCallback)action).doFrame(frameTimeNanos);  
  10.         } else {  
  11.             ((Runnable)action).run();  
  12.         }  
  13.     }  
  14. }  

我们知道Choreographer对外提供了两个接口函数用于注册指定的Callback,postCallback()用于注册Runnable对象,而postFrameCallback()函数用于注册FrameCallback对象,无论注册的是Runnable对象还是FrameCallback对象,在CallbackRecord对象中统一装箱为Object类型。在执行其回调函数时,就需要区别这两种对象类型,如果注册的是Runnable对象,则调用其run()函数,如果注册的是FrameCallback对象,则调用它的doFrame()函数。

Vsync请求过程

我们知道在Choreographer构造函数中,构造了一个FrameDisplayEventReceiver对象,用于请求并接收Vsync信号,Vsync信号请求过程如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private void scheduleVsyncLocked() {  
  2.     //申请Vsync信号  
  3.     mDisplayEventReceiver.scheduleVsync();  
  4. }  

FrameDisplayEventReceiver继承于DisplayEventReceiver类,Vsync请求在DisplayEventReceiver中实现。

frameworks\base\core\java\android\view\ DisplayEventReceiver.java

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void scheduleVsync() {  
  2.     if (mReceiverPtr == 0) {  
  3.         Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "  
  4.                 + "receiver has already been disposed.");  
  5.     } else {  
  6.         //通过Jni方式调用native层的NativeDisplayEventReceiver对象来请求VSync  
  7.         nativeScheduleVsync(mReceiverPtr);  
  8.     }  
  9. }  

frameworks\base\core\jni\ android_view_DisplayEventReceiver.cpp

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jint receiverPtr) {  
  2.     //得到NativeDisplayEventReceiver对象指针  
  3.     sp<NativeDisplayEventReceiver> receiver =  
  4.             reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);  
  5.     //通过NativeDisplayEventReceiver请求VSync  
  6.     status_t status = receiver->scheduleVsync();  
  7.     if (status) {  
  8.         String8 message;  
  9.         message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);  
  10.         jniThrowRuntimeException(env, message.string());  
  11.     }  
  12. }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t NativeDisplayEventReceiver::scheduleVsync() {  
  2.     if (!mWaitingForVsync) {  
  3.         ALOGV("receiver %p ~ Scheduling vsync."this);  
  4.         // Drain all pending events.  
  5.         nsecs_t vsyncTimestamp;  
  6.         uint32_t vsyncCount;  
  7.         readLastVsyncMessage(&vsyncTimestamp, &vsyncCount);  
  8.         status_t status = mReceiver.requestNextVsync();  
  9.         if (status) {  
  10.             ALOGW("Failed to request next vsync, status=%d", status);  
  11.             return status;  
  12.         }  
  13.         mWaitingForVsync = true;  
  14.     }  
  15.     return OK;  
  16. }  

VSync请求过程又转交给了DisplayEventReceiver

frameworks\native\libs\gui\ DisplayEventReceiver.cpp

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t DisplayEventReceiver::requestNextVsync() {  
  2.     if (mEventConnection != NULL) {  
  3.         mEventConnection->requestNextVsync();  
  4.         return NO_ERROR;  
  5.     }  
  6.     return NO_INIT;  
  7. }  

这里又通过IDisplayEventConnection接口来请求Vsync信号,IDisplayEventConnection实现了Binder通信框架,可以跨进程调用,因为Vsync信号请求进程和Vsync产生进程有可能不在同一个进程空间,因此这里就借助IDisplayEventConnection接口来实现。下面通过图来梳理Vsync请求的调用流程:


0 0