Android黄油计划之Choreographer原理解析

   搞客户端开发,时间也有点了,但是每次想起来,总感觉自己掌握的东西零零散散,没有一点集在的感觉,应用层的懂,framework的也懂,框架啥的了解一点,分层的思想也有一些,JVM的原理啊,内存分配和管理啊,运行机制啊啥的也知道一点,每次下班或者没事了,就在考虑,自己应该有一个主攻方向,往这个方向集中发展一下,首选的几个目标应该是非常清楚的,我们要掌握android,那么关于android的View机制、动画原理这些都是必须要掌握的,所以呢,自己想在这几个方面花些时间,好好研究一下,这样才能使自己更具竞争力。

     好了,不管是要了解View机制,还是android动画,我们应该都需要有Choreographer的知识,明白系统刷新机制到底是怎么样的,这样才能对其他方面有更好的辅助。本章博客,我们就来学习一下Android中的Choreographer的运行机制。

     我们都知道,应用层的一个Activity对应一个根View(也就是一个DecorView)、一个WindowState、一个ViewRootImpl,每个对象都非常重要,都是在Activity添加过程中重量级的对象,DecorView是当前Activity的根View,它里面管理着当前界面的View树;WindowState对象是当前Activity窗口在系统侧WindowManagerService中代理对象;ViewRootImpl则肩负着View的标准三步曲的处理和事件分发,而View绘制也是由Choreographer指导的,Choreographer的英文意思就是编舞者、舞蹈指挥,看着非常形象。那我们就从Choreographer对象的构建开始说起吧,它的构建是在ViewRootImpl的构造方法中的,代码如下:

  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
          
          
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mDisplayAdjustments = display.getDisplayAdjustments();
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
mWidth = -1;
mHeight = -1;
mDirty = new Rect();
mTempRect = new Rect();
mVisRect = new Rect();
mWinFrame = new Rect();
mWindow = new W(this);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
// [+LEUI-9331]
mPreBlurParams = new BlurParams();
// [-LEUI-9331]
mFirst = true; // true for the first time the view is added
mAdded = false;
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityInteractionConnectionManager =
new AccessibilityInteractionConnectionManager();
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
mHighContrastTextManager = new HighContrastTextManager();
mAccessibilityManager.addHighTextContrastStateChangeListener(
mHighContrastTextManager);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
/**
* M: increase instance count and check log property to determine
* whether to enable/disable log system. @{
*/
mIdent = sIdent++;
checkViewRootImplLogProperty();
if (LOCAL_LOGV) {
enableLog(true, "a");
}
if (DEBUG_LIFECYCLE) {
Log.v(TAG, "ViewRootImpl construct: context = " + context + ", mThread = " + mThread
+ ", mChoreographer = " + mChoreographer + ", mTraversalRunnable = "
+ mTraversalRunnable + ", this = " + this);
}
}
来自CODE的代码片
snippet_file_0.txt

     从构造方法中可以看到Choreographer是单例模式的,也就是一个ViewRootImpl对象对应一个Choreographer,当界面需要重绘时,都会调用到ViewRootImp类的scheduleTraversals()方法,这里的实现也比较简单,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
          
          
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
scheduleConsumeBatchedInput();
}
}
来自CODE的代码片
snippet_file_0.txt

     mTraversalScheduled表示是否已经发起重绘,每次scheduleTraversals()方法调用之后,就会将它置为true,然后在下次调用doTraversal()又先将它置为false,然后调用mChoreographer.postCallback()添加一个Runnable,请注意,第一个参数是Choreographer.CALLBACK_TRAVERSAL,在Choreographer当前,添加的类型一共有三种,分别是:CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL,分别表示事件回调、动画回调、绘制回调。postCallback()方法是转而调用postCallbackDelayed()方法的,最后一个参数delayMillis传的是0,表示当前的重绘不需要延时,我们跟进去看一下添加的postCallbackDelayed()方法的代码:

  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
          
          
/**
* Posts a callback to run on the next frame after the specified delay.
* <p>
* The callback runs once then is automatically removed.
* </p>
*
* @param callbackType The callback type.
* @param action The callback action to run during the next frame after the specified delay.
* @param token The callback token, or null if none.
* @param delayMillis The delay time in milliseconds.
*
* @see #removeCallback
* @hide
*/
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
来自CODE的代码片
snippet_file_0.txt
     首先判断参数action是否为空,action就是我们要回调的对象,回调对象都为空了,那我们还干啥呢?其实判断callbackType,在整个过程中,只定义了上面描述的三种类型的事件,如果传入的type值不符合,那就抛出一个IllegalArgumentException("callbackType is invalid")异常。参数正常了,继续调用postCallbackDelayedInternal()进一步处理。postCallbackDelayedInternal()方法的代码如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
          
          
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
来自CODE的代码片
snippet_file_0.txt
     此处获取当前时间,然后加上要延迟的时间,作为当前Callback的时间点,以这个时间点作为标准,把Callback对象添加到mCallbackQueues[callbackType]队列当中,这块的逻辑和Looper、MessageQueue、Handler中添加Message的逻辑很相似,大家可以对比学习。然后判断dueTime <= now,这块的逻辑看了半天,我确实没看懂,dueTime会有比now小的情况吗,也就是传进来的delayMillis小于0,再往上讲,就是当前要添加的回调要在上一次添加的回调之前,这感觉不太可能吧?如果有弄懂的朋友,烦请解答一下。此处应该是执行else分支,往当前的队列中添加一个Message,那么通过Handler机制就会进行处理,此处的mHandler是一个FrameHandler对象,我们来看一下FrameHandler的代码:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
          
          
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
来自CODE的代码片
snippet_file_0.txt
     这里的message消息也比较简单,MSG_DO_FRAME指系统在没有使用Vsync机制的时候,使用异步消息来刷新屏幕,当然,大家一定要理解,此处的刷新其实只是刷新屏幕工作的很小一部分,只是回调ViewRootImpl方法中添加的Runnable对象,最终是调用根View的draw方法,让每个子View有把自己的图像元素填充到分配好的显存当中,而要完全显示,还有很多工作要作,最终是在SurfaceFlinger类中对所有窗口的View进行合成,然后渲染,最终post到FrameBuffer上,才能显示出来的;MSG_DO_SCHEDULE_VSYNC当然就是指系统使用Vsync来刷新了;MSG_DO_SCHEDULE_CALLBACK就是指添加Callback或者FrameCallback完成的消息了。好了,我们继续看MSG_DO_SCHEDULE_CALLBACK的消息处理,它是调用doScheduleCallback(msg.arg1)来进行处理的,msg.arg1是刚才添加消息时的类型。我们整个看一下handleMessage()方法的代码,发现非常简单,这也是一个非常好的习惯,我们平时的代码当中,也应该尽量这样实现,这样一眼就可以看出来这个方法所要作的事情,把具体的处理放到每个细节方法中去。我们来看一下doScheduleCallback()方法的实现:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
          
          
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
来自CODE的代码片
snippet_file_0.txt
     mFrameScheduled和ViewRootImpl的scheduleTraversals()方法中的变量mTraversalScheduled作用是一样的,也是判断当前是否正在执行添加,然后调用(mCallbackQueues[callbackType].hasDueCallbacksLocked(now))判断是否已处理过Callback事务,该方法的判断也很简单,(mHead != null && mHead.dueTime<= now),如果当前队列头不为空,并且队列头元素的时间点小于当前的时间点,那就说明是之前添加的,则需要对它进行处理;相反,如果队列头为空或者添加的时间点大于当前的时间点,也就是要延迟处理,则不需要任何操作。条件符合的话,就调用scheduleFrameLocked(now)进一步处理,我们来看一下scheduleFrameLocked()方法的实现:
  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
          
          
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
if (DEBUG) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
来自CODE的代码片
snippet_file_0.txt
     此片一开始就把mFrameScheduled赋值为true,表示事务开始执行了,那么上面doScheduleCallback()方法当中的代码此该就不会再执行了。接下来的逻辑以USE_VSYNC分开,意思也非常明了,就是系统是否使用Vsync刷新机制,它是通过获取系统属性得到的,private static final boolean USE_VSYNC =  SystemProperties.getBoolean("debug.choreographer.vsync",true)。如果使用了Vsync垂直同步机制,则一步判断当前线程是否具备消息循环,如果有消息循环,则立即请求下一次Vsync信号,如果不具有消息循环,则通过当前进程的主线程请求Vsync信号;如果没有使用Vsync机制,则使用异步消息延时执行屏幕刷新。是否具有消息循环是通过调用isRunningOnLooperThreadLocked()方法完成判断的,它的实现很简单,return Looper.myLooper() == mLooper。因为当Choreographer对象在创建的时候,参数looper就是调用Looperlooper = Looper.myLooper()获取回来的,也就是说当前进程肯定是有消息循环的,所以此处的判断为true,其他两个分支:当前线程不具备消息循环和系统未使用Vsync同步机制的逻辑,我们就不分析了,大家有兴趣的话,可以自己跟踪一下。进入if分支,继续调用scheduleVsyncLocked()方法进行处理,它的实现非常简单,就是调用mDisplayEventReceiver.scheduleVsync()来请求下一次Vsync信号。      看到这里,是不是感觉逻辑有点多了,开始乱了,转来转去的,系统到底要干啥?呵呵,我们暂停下来梳理一下,系统作了这么多事情最终的目的就是在下一次Vsync信号到来的时候,将Choreographer当中的三个队列中的事务执行起来,这些事务是应用层ViewRootImpl在scheduleTraversals()方法中添加进去的,在Choreographer当中,我们要先将外边传进来的Callback放入队列,然后就要去请求Vsync信号,因为Vsync信号是定时产生的,你不请求,它就不会理你,当然你收不到回调,也就不知道啥时候通知ViewRootImpl执行View的measure、layout、draw了,这样说一下,大家清楚我们要干什么了吗?我第一次看Choreographer类的代码时候,看了半天,也是乱了,所以这里大概理一下。      好,我们搞清楚目的了,继续往前走,我们现在已经将Callback添加到队列中了,下一步要作的就是请求Vsync信号了。mDisplayEventReceiver是一个FrameDisplayEventReceiver对象,我们来看一下它的代码定义:
  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
          
          
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper) {
super(looper);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
// Ignore vsync from secondary display.
// This can be problematic because the call to scheduleVsync() is a one-shot.
// We need to ensure that we will still receive the vsync from the primary
// display which is the one we really care about. Ideally we should schedule
// vsync for a particular display.
// At this time Surface Flinger won't send us vsyncs for secondary displays
// but that could change in the future so let's log a message to help us remember
// that we need to fix this.
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
Log.d(TAG, "Received vsync from secondary display, but we don't support "
+ "this case yet. Choreographer needs a way to explicitly request "
+ "vsync for a specific display to ensure it doesn't lose track "
+ "of its scheduled vsync.");
scheduleVsync();
return;
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
long now = System.nanoTime();
if (timestampNanos > now) {
Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ " ms in the future! Check that graphics HAL is generating vsync "
+ "timestamps using the correct timebase.");
timestampNanos = now;
}
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
来自CODE的代码片
snippet_file_0.txt
     我们可以看到这里的mTimestampNanos时间定义都是纳秒级别的,因为Vsync信号是用来同步屏幕刷新频率的,所以对时间的要求非常高,才采用了纳秒级别的,如果大家对Vsync信号的产生机制不了解的话,可以看我前面的博客:Vsync垂直同步信号分发和SurfaceFlinger响应执行渲染流程分析(一),mDisplayEventReceiver类变量是在Choreographer的构造方法中赋值的,我们继续来看它的scheduleVsync()方法的实现,因为FrameDisplayEventReceiver类是继承DisplayEventReceiver的,而它没用对scheduleVsync()方法重写,所以是调用父类的:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
          
          
/**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
*/
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
来自CODE的代码片
snippet_file_0.txt
     它的实现很简单,判断描述符mReceiverPtr是否合法,如果非法就打印日志,什么也不作了,合法的话,就继续调用native方法nativeScheduleVsync(mReceiverPtr)来请求Vsync信号。nativeScheduleVsync()方法实现在android_view_DisplayEventReceiver.cpp当中,是通过定义JNINativeMethod gMethods[]来定义方法调用指针的,因为此类的代码不多,这里就全部贴出来,方便大家查看:
   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
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
          
          
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "DisplayEventReceiver"
//#define LOG_NDEBUG 0
#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/threads.h>
#include <gui/DisplayEventReceiver.h>
#include "android_os_MessageQueue.h"
namespace android {
// Number of events to read at a time from the DisplayEventReceiver pipe.
// The value should be large enough that we can quickly drain the pipe
// using just a few large reads.
static const size_t EVENT_BUFFER_SIZE = 100;
static struct {
jclass clazz;
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
} gDisplayEventReceiverClassInfo;
class NativeDisplayEventReceiver : public LooperCallback {
public:
NativeDisplayEventReceiver(JNIEnv* env,
jobject receiverObj, const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
status_t scheduleVsync();
protected:
virtual ~NativeDisplayEventReceiver();
private:
jobject mReceiverObjGlobal;
sp<MessageQueue> mMessageQueue;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
virtual int handleEvent(int receiveFd, int events, void* data);
bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);
void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
};
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
jobject receiverObj, const sp<MessageQueue>& messageQueue) :
mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
mMessageQueue(messageQueue), mWaitingForVsync(false) {
ALOGV("receiver %p ~ Initializing input event receiver.", this);
}
NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mReceiverObjGlobal);
}
status_t NativeDisplayEventReceiver::initialize() {
status_t result = mReceiver.initCheck();
if (result) {
ALOGW("Failed to initialize display event receiver, status=%d", result);
return result;
}
int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
this, NULL);
if (rc < 0) {
return UNKNOWN_ERROR;
}
return OK;
}
void NativeDisplayEventReceiver::dispose() {
ALOGV("receiver %p ~ Disposing display event receiver.", this);
if (!mReceiver.initCheck()) {
mMessageQueue->getLooper()->removeFd(mReceiver.getFd());
}
}
status_t NativeDisplayEventReceiver::scheduleVsync() {
if (!mWaitingForVsync) {
ALOGV("receiver %p ~ Scheduling vsync.", this);
// Drain all pending events.
nsecs_t vsyncTimestamp;
int32_t vsyncDisplayId;
uint32_t vsyncCount;
processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount);
status_t status = mReceiver.requestNextVsync();
if (status) {
ALOGW("Failed to request next vsync, status=%d", status);
return status;
}
mWaitingForVsync = true;
}
return OK;
}
int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. "
"events=0x%x", events);
return 0; // remove the callback
}
if (!(events & Looper::EVENT_INPUT)) {
ALOGW("Received spurious callback for unhandled poll event. "
"events=0x%x", events);
return 1; // keep the callback
}
// Drain all pending events, keep the last vsync.
nsecs_t vsyncTimestamp;
int32_t vsyncDisplayId;
uint32_t vsyncCount;
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, id=%d, count=%d",
this, vsyncTimestamp, vsyncDisplayId, vsyncCount);
mWaitingForVsync = false;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
return 1; // keep the callback
}
bool NativeDisplayEventReceiver::processPendingEvents(
nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) {
bool gotVsync = false;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("receiver %p ~ Read %d events.", this, int(n));
for (ssize_t i = 0; i < n; i++) {
const DisplayEventReceiver::Event& ev = buf[i];
switch (ev.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
// Later vsync events will just overwrite the info from earlier
// ones. That's fine, we only care about the most recent.
gotVsync = true;
*outTimestamp = ev.header.timestamp;
*outId = ev.header.id;
*outCount = ev.vsync.count;
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected);
break;
default:
ALOGW("receiver %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
}
}
}
if (n < 0) {
ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
}
return gotVsync;
}
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ALOGV("receiver %p ~ Invoking vsync handler.", this);
env->CallVoidMethod(mReceiverObjGlobal,
gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}
void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ALOGV("receiver %p ~ Invoking hotplug handler.", this);
env->CallVoidMethod(mReceiverObjGlobal,
gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected);
ALOGV("receiver %p ~ Returned from hotplug handler.", this);
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
receiverObj, messageQueue);
status_t status = receiver->initialize();
if (status) {
String8 message;
message.appendFormat("Failed to initialize display event receiver. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}
static void nativeDispose(JNIEnv* env, jclass clazz, jlong receiverPtr) {
sp<NativeDisplayEventReceiver> receiver =
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
receiver->dispose();
receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object
}
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
sp<NativeDisplayEventReceiver> receiver =
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
status_t status = receiver->scheduleVsync();
if (status) {
String8 message;
message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status);
jniThrowRuntimeException(env, message.string());
}
}
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
"(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)J",
(void*)nativeInit },
{ "nativeDispose",
"(J)V",
(void*)nativeDispose },
{ "nativeScheduleVsync", "(J)V",
(void*)nativeScheduleVsync }
};
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
var = jclass(env->NewGlobalRef(var));
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
int register_android_view_DisplayEventReceiver(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver",
gMethods, NELEM(gMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver");
GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync,
gDisplayEventReceiverClassInfo.clazz,
"dispatchVsync", "(JII)V");
GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchHotplug,
gDisplayEventReceiverClassInfo.clazz,
"dispatchHotplug", "(JIZ)V");
return 0;
}
} // namespace android
来自CODE的代码片
snippet_file_0.txt
     我们来看一下nativeScheduleVsync方法的定义,{ "nativeScheduleVsync", "(J)V", (void*)nativeScheduleVsync },这里需要说明一下,java方法和JNI方法存在着对应关系,"(J)V"括号里边的表示该方法的入参,括号外边的表示返回值J表示long,而返回值V表示Void,关于这个,大家可以看我之前的博客:JNI字段描述符“([Ljava/lang/String;)V”,也是转载别人的,呵呵。好了,我们继续看这个方法的实现,它将java层传进来的描述符强制转换为NativeDisplayEventReceiver对象,这样的处理在JNI当中是非常多见的,大家要熟悉。然后调用它的scheduleVsync()方法,最后根据返回值判断当前请求Vsync信号是否成功,如果status非0,则抛出RuntimeException异常。很明显,我们从这都可以猜出,正常情况下,返回的status应该为0了。      我们继续来看NativeDisplayEventReceiver::scheduleVsync()方法的处理逻辑。首先检查mWaitingForVsync,如果当前正在请求Vsync信号,则就不需要重复请求了,只有在当前未请求的时候,才需要发出新的请求,然后调用processPendingEvents()将当前队列中还存在receiver处理掉,因此方法与我们的流程不相关,这里就不展开了,大致是使用pipe机制将mReceiver中还存在的receiver一一读出,大家如果了解Linux机制的话,就知道pipe机制对应了两个管道,管道中的数据被读出之后,也就相应的从管道中移除了,所以不需要两端对数据做任何移除的处理,每一个receiver处理完成后,就设置一下gotVsync= true,
*outTimestamp = ev.header.timestamp,*outId = ev.header.id,*outCount = ev.vsync.count,gotVsync的意思就是当前的receiver已经收到Vsync信号通知了。好了,我们回到主流程,scheduleVsync()方法当中处理完队列中的receiver后,就开始调用mReceiver.requestNextVsync()请求新的Vsync信号了,mReceiver是一个DisplayEventReceiver对象,我们来看一下requestNextVsync()方法的实现,因这个类的代码也很少,这里就直接全部贴出来了:
  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
          
          
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <utils/Errors.h>
#include <gui/BitTube.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
DisplayEventReceiver::DisplayEventReceiver() {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != NULL) {
mEventConnection = sf->createDisplayEventConnection();
if (mEventConnection != NULL) {
mDataChannel = mEventConnection->getDataChannel();
}
}
}
DisplayEventReceiver::~DisplayEventReceiver() {
}
status_t DisplayEventReceiver::initCheck() const {
if (mDataChannel != NULL)
return NO_ERROR;
return NO_INIT;
}
int DisplayEventReceiver::getFd() const {
if (mDataChannel == NULL)
return NO_INIT;
return mDataChannel->getFd();
}
status_t DisplayEventReceiver::setVsyncRate(uint32_t count) {
if (int32_t(count) < 0)
return BAD_VALUE;
if (mEventConnection != NULL) {
mEventConnection->setVsyncRate(count);
return NO_ERROR;
}
return NO_INIT;
}
status_t DisplayEventReceiver::requestNextVsync() {
if (mEventConnection != NULL) {
mEventConnection->requestNextVsync();
return NO_ERROR;
}
return NO_INIT;
}
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
return DisplayEventReceiver::getEvents(mDataChannel, events, count);
}
ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel,
Event* events, size_t count)
{
return BitTube::recvObjects(dataChannel, events, count);
}
ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
Event const* events, size_t count)
{
return BitTube::sendObjects(dataChannel, events, count);
}
// ---------------------------------------------------------------------------
}; // namespace android
来自CODE的代码片
snippet_file_0.txt
     requestNextVsync()方法中直接调用mEventConnection->requestNextVsync()来请求Vsync信号,mEventConnection对象是在DisplayEventReceiver类的构造函数中创建的,mEventConnection = sf->createDisplayEventConnection(),sf就是SurfaceFlinger对象,SurfaceFlinger类的createDisplayEventConnection()实现也非常简单,就是调用mEventThread->createEventConnection(),这又回到我们之前的博客了,大家可以去看一下。      EventThread一直在无限循环threadLoop()中请求Vsync信号的,当收到一个Vsync信号后,会调用status_t err = conn->postEvent(event)来进行分发,conn也就是上面的EventThread::Connection对象了,最后经过处理,回调到NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void*data)方法当中,这里同样processPendingEvents()处理完队列中的回调后,就调用dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount)开始分发了,在NativeDisplayEventReceiver::dispatchVsync()这个方法中是通过当前的native层的执行环境env回调到java层的,env->CallVoidMethod(mReceiverObjGlobal,
gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count),再往下就回调到java层中DisplayEventReceiver类的dispatchVsync()方法中了。它里边的实现就是调用onVsync(),而FrameDisplayEventReceiver复写了onVsync()方法,所以就执行到Choreographer.FrameDisplayEventReceiver中的onVsync()方法了。
     转了好大一圈,我们终于又从native层回来了。好,我们继续java层往下分析,Vsync信号拿回来了,大家应该也知道,我们的目的快达到了!!      onVsync()方法中以this为对象,向mHandler中添加了一个消息,消息处理的时候,就会调用它的run()方法了。run方法中直接调用doFrame()来进行处理。我们来看一下它的实现:
  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
          
          
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
if (DEBUG) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
scheduleVsyncLocked();
return;
}
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
if (DEBUG) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
来自CODE的代码片
snippet_file_0.txt
     如果(frameTimeNanos < mLastFrameTimeNanos)满足,则说明我们已经错过了本次的Vsync信号了,那么这种情况下,就什么也不用处理,重新获取下一次信号了。如果没有错过的话,那就进一步三次调用doCallbacks()分别对应三种事件类型来分发了。三种事件的顺序也是定义的顺序:CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL,这也是他们的处理优先级,输入事件放在第一,也是为了能尽快响应用户的操作,但是即使这样,Android的流畅性还是不如IOS,当然,这个原因就是其他方面的了,我们这里就不探讨了。我们来看一下doCallbacks()方法的实现:
  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
          
          
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
final long now = SystemClock.uptimeMillis();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
}
try {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
}
}
来自CODE的代码片
snippet_file_0.txt
     这里就是将每种类型的事件队列中的元素取出来,通过for循环一一调用他们的run()方法了,调用完成后,将队列中的Callback回收掉。而这里的CallbackRecord对象就是我们在ViewRootImpl类当中添加的InvalidateOnAnimationRunnable、mConsumedBatchedInputRunnable、mTraversalRunnable这三类对象了,那么回到View的流程中,收到Vsync信号后,就会回调mTraversalRunnable的run()方法,再次发起一次measure、layout、draw流程,那么也就和Vsync信号对接上了。      好了,到这里呢,我们整个流程也就分析完了,希望对大家有所帮助,谢谢大家!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值