Android Choreographer 源码笔记

  1. 之前有写过一篇粗略分析的文章:
    http://blog.csdn.net/fyfcauc/article/details/43307253
    不过还是不够,这次再专门细读一下:

  2. Choreographer主要被外部使用的函数是postCallback(…), 就是在Choreographer中schedule一个Task,这个Task何时运行,则是是由Choreographer来自行安排,满足作Sync的需求

  3. postCallback(…) -> postCallbackDelayed(…) ->postCallbackDelayedInternal(…, long delayMillis), postCallbackDelayedInternal的具体步骤:

    • 首先获取mLock来同步操作mCallbackQueues这个Queue, 获取当前的时间存为now(SystemClock.uptimeMillis(), 再根据输入的delayMillis获得一个到期时间dueTime), 然后将dueTime以及输入的Task加入到mCallbackQueues[输入的callbackType]对应的Queue中.
    • 如果发现dueTime <= now,那么会直接scheduleFrameLocked(now)。
    • 否则, 会生成一个MSG_DO_SCHEDULE_CALLBACK类型的,runnable为输入的Task的Message,并且该msg的arg1是输入的callbackType.
    • 将Messgae设置为异步的(setAsynchronous(true)).
    • 然后将这个Message post到Choreographer的handler中(这个handler基于的线程是传入的Looper基于的线程,在这里就是UI线程).
  4. Choreographer不能直接构造(private ctor),而是使用getInstance()获得当前线程的TLS, 其构造时传入的looper也是调用getInstance()所在线程的looper, 因此在ViewRootImpl中使用的mChoreographer基于的looper就是ViewRootImpl构造时所在的线程,这里就是UI线程. 因此Choreographer的主体操作(doFrame这种重构UI的操作)就是在主线程的handler被post已经执行的.

  5. mHandler的类型是自定义的FrameHandler:

    • 其对MSG_DO_SCHEDULE_CALLBACK的处理是doScheduleCallback(msg.arg1):
      • doScheduleCallback(…)操作也会获取mLock, 然后检查当前是否已经scheduke过一个FrameTask了(mFrameScheduled), 如果还没有schedule过, 那么就获取现在的时间now, 然后以此来检测mCallbackQueues[callbackType]这个对应的CallType的Queue里是否已经有了到期需要执行的Task,如果有,那么就schedule一个FrameTask(scheduleFrameLocked(now))
  6. scheduleFrameLocked(long now):

    • 也需要先检查是否已经schedule了FrameTask(mFrameScheduled), 如果没有,那么先把mFrameScheduled=true来表明已经schedule了,然后开始真正的schedule过程.
    • 如果使用了USE_VSYNC(4.0以后为true),那么检查当前运行的thread是不是就是choregrapher的looper所在的线程(isRunningOnLooperThreadLocked()),如果是, 那么直接调用scheduleVsyncLocked().
    • 否则向mHandler schedule一个MSG_DO_SCHEDULE_VSYNC类型的Message, 而这个Msg在FrameHandler中的处理过程就是doScheduleVsync(),doScheduleVsync()很简单,获取mLock以后会检测是否已经schedule过FrameTask,如果没有,才会调用scheduleVsyncLocked()(其实和上面在自己线程的处理过程是一样的).
  7. scheduleVsyncLocked()这个操作就是调用了mDisplayEventReceiver.scheduleVsync(): 而mDisplayEventReceiver是一个在初始化时就以传入的looper为参数构造的一个DisplayEventReceiver(自定义了其子类FrameDisplayEventReceiver).

    • scheduleVsync()这个函数进一步跑到了native层,不过做的事情注释说的很清楚: Schedules a single vertical sync pulse to be delivered when the next display frame begins. 即在下一帧开始的时候,会要求其deliver一个同步脉冲
    • 上面说的这个同步脉冲其实就是一个回调,这个回调函数也在DisplayEventReceiver中,就是其onVsync(…)方法,这个方法(空)在FrameDisplayEventReceiver被具体实现了.
  8. FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable 实现了同步脉冲的回调函数onVsync(…):

    • 获取系统的当前时间(System.nanoTime(), 纳秒级了,以为同步脉冲回传的timeStamp是纳秒级的),然后比较同步脉冲的timeStamp和now, 如果timeStamp比now还要大, 这应该是不对的,但是考虑到时间本身不能做到很精确也可以接受,不过会将timeStamp再设置为now.
    • 将mHavePendingVsync设置为true表明当前有一个pending的同步脉冲没有被处理,如果之前已经有了pending的,那么这一次的就可以直接忽略的了.
    • 基于当前的handler构造一个Message,然后将其post到handler上, 而其到期的运行时间就是传入的timestamp,这就实现了想要的同步效果,不是想啥时候运行就啥时候运行,要听sync信号的话, 不然一窝蜂乱跑,效率会有问题, Message的runnable则是FrameDisplayEventReceiver自己,这也是其实现了Runnable的原因(独立搞一个Runnable也完全可以).
    • 其run()函数做的就是真正在下一帧的同步脉冲到达时(这个时间就是同步脉冲传过来的timeStamp)是要做的操作:doFrame(mTimestampNanos, mFrame) , 同时还会将mHavePendingVsync设置为false
  9. doFrame(…)也要获取mLock锁,然后检测是否之前schedule过FrameTask了,如果没有,那么就啥也不干(没啥可干的)。

    • 然后获取当前的时间, 并减去 同步脉冲给的时间,得到一个抖动值(jitter**反映的是一次同步脉冲的发生到真正执行操作之间的延迟), 如果抖动值已经大于了mFrameIntervalNanos((long)(1000000000 / getRefreshRate()), 就是每一帧持续的时间,以纳秒为单位), 那么就可以认为是出现了跳帧**,并且在跳帧大于一定阈值的情况下,会在log中写入.
    • jitterNanos % mFrameIntervalNanos得到理想中情况这一帧已经开始了多长时间. 再用startNanos - lastFrameOffset得到这一帧应该的开始时间并设置给frameTimeNanos
    • 然后再比较,如果frameTimeNanos < mLastFrameTimeNanos, 那么这一帧应该是错乱了,再scheduleVsyncLocked()等待下一次vsync.
    • mFrameScheduled设置为false, 也即在这之后,可以schedule新的FrameTask了, 然后mLastFrameTimeNanos = frameTimeNanos.
    • 下面就是依次处理三类CallType的task了:
      • doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); 输入(用户的触摸等操作)最优先被处理
        • doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); 然后是帧动画(可以看到ValueAnimator以及View等都使用这个type)
        • doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); 最后才是measure/layout/draw之类的
    • doCallbacks(…):
      • 将mCallbackQueues[callbackType]中到期的Callback全部取出来进行遍历处理(调用CallbackRecord的run函数.) CallbackRecord的run函数会判断自己的token值, 如果是FRAME_CALLBACK_TOKEN, 那么将action对象转为FrameCallback执行doFrame,否则转为Runnable执行run.
      • ViewRootImpl调用的是postCallback, 因此这里的是Runnable,执行的就是ViewRootImpl中的mTraversalRunnable的run()->doTraversal().
  10. 根据上面的分析来讨论一些情况:

    • 在Choreographer 处理doCallbacks(Choreographer.CALLBACK_INPUT/CALLBACK_ANIMATION, frameTimeNanos)时调用了requestLayout()/invalidate(), 那么就会ViewRootImpl->scheduleTraversals()->(如果之前没有schedule过的话)->mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)(要注意的是doCallback发生的线程也是ViewRootImpl的主线程) 虽然这一步会mLock加锁,但是doFrame在处理到doCallbacks(XXXX)时已经释放了mLock并且mFrameScheduled也变为了false, 接下来就是将mTraversalRunnable加入到mCallbackQueues[callbackType]队列中, 这时候的dueTime还是等于now的,于是会scheduleFrameLocked(now).如同前面分析的,这一步也会出发一次同步脉冲请求回调,至此scheduleTraversals()这个同步调用就结束了,然后就会继续到doCallbacks(CALLBACK_TRAVERSAL), 这一步将所有到期的Task都执行一遍,也包括了之前在CALLBACK_INPUT/CALLBACK_ANIMATION中加入的那个新的Task.
    • 但是如果在doCallbacks(CALLBACK_TRAVERSAL)的时候进行requestLayout()/invalidate(), 那么这时候mTraversalScheduled已经是false了,也会postCallback,但是达不到在这个drawCircle就生效的效果了(因为要处理的callback列表已经取出来了,新加入的callback并不在里面),而要等待下一个同步脉冲回调的处理(已经被触发了,只需要等待).
  11. 丢帧/卡 分析,可以看到FrameDisplayEventReceiver的onVsync也是一个被post到Choreographer所在的Handler的Task, 这样的话,如果前面的Task(就是那3个doCallback)花费了太长时间的话,那么onVsync的实际运行时间一定会比自己的timeStamp(“这个是同步脉冲在新的一帧开始时给的时间”)要晚,如果晚很多的,就会出现这些UI响应的实际操作时间和Vsync预定的执行时间差很远,就会出现卡/丢帧.

  12. Choregrapher请求一次VSYNC信号是通过自己内部定义的FrameDisplayEventReceiver的scheduleVsync()来实现一次VSYNC信号的schedule:

    • FrameDisplayEventReceiver extends DisplayEventReceiver.
    • DisplayEventReceiver的requestNextVsync()则是调用mEventConnection->requestNextVsync().
    • mEventConnection在DisplayEventReceiver的构造函数中被初始化: mEventConnection = sf(其实就是SurfaceFlinger)->createDisplayEventConnection();
    • 在SurfaceFlinger中的实现是: mEventThread->createEventConnection().
    • mEventThread则是在SurfaceFlinger中的init()中被new的EventThread(vsyncSrc).
    • EventThread的createEventConnection()返回new Connection(const_cast(this)). Connection则是EventThread内部定义的类, 通过Connection的requestNextVsync()调用的还是EventThread的requestNextVsync(this(这个是Connection自己))
    • EventThread的requestNextVsync(const sp& connection)会检测connection->count 是否<0(<0代表Connection对VSYNC信号不感兴趣), 如果<0, 即此Connection当前对VSYNC信号并不感兴趣,那么需要让其对VSYNC感兴趣,将Connection的count设置为0(one-shot), 并调用mCondition的broadcast()来触发EventThread的threadLoop中的waitForEvent(…)来使其能够真正的scheduleu一次VSYNC信号, 并在信号来到时告知此Connection.

你可能感兴趣的:(多线程,Android,Layout,Android,Android,Draw,Android,Schedule)