美文网首页
Android8.1 Choreographer机制与源码分析

Android8.1 Choreographer机制与源码分析

作者: scarecrowtb | 来源:发表于2018-07-04 16:08 被阅读0次

    这篇博文是参考别人的博客,结合源码自己又走了一遍,仅供自己记录学习。

    我们要掌握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的构造方法中的,代码如下

    public ViewRootImpl(Context context, Display display) {
            mContext = context;
            mWindowSession = WindowManagerGlobal.getWindowSession();
            mDisplay = display;
            mBasePackageName = context.getBasePackageName();
            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();
            mFirst = true; // true for the first time the view is added
            mAdded = false;
            mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                    context);
            mAccessibilityManager = AccessibilityManager.getInstance(context);
            mAccessibilityManager.addAccessibilityStateChangeListener(
                    mAccessibilityInteractionConnectionManager, mHandler);
            mHighContrastTextManager = new HighContrastTextManager();
            mAccessibilityManager.addHighTextContrastStateChangeListener(
                    mHighContrastTextManager, mHandler);
            mViewConfiguration = ViewConfiguration.get(context);
            mDensity = context.getResources().getDisplayMetrics().densityDpi;
            mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
            mFallbackEventHandler = new PhoneFallbackEventHandler(context);
            // 这里获取了Choreographer的实例
            mChoreographer = Choreographer.getInstance();
            mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    
            if (!sCompatibilityDone) {
                sAlwaysAssignFocus = true;
    
                sCompatibilityDone = true;
            }
    
            loadSystemProperties();
            ViewDebugManager.getInstance().debugViewRootConstruct(mTag, context,
                             mThread, mChoreographer, mTraversalRunnable, this);
        }
    

    看一下Choreographer.getInstance(),获取的是一个单例。

        public static Choreographer getInstance() {
            return sThreadInstance.get();
        }
    

    sThreadInstance是一个ThreadLocal对象,ThreadLocal 是线程的局部变量, 是每一个线程所单独持有的,其他线程不能对其进行访问.

        private static final ThreadLocal<Choreographer> sThreadInstance =
                new ThreadLocal<Choreographer>() {
            @Override
            protected Choreographer initialValue() {
                Looper looper = Looper.myLooper();
                if (looper == null) {
                    throw new IllegalStateException("The current thread must have a looper!");
                }
                return new Choreographer(looper, VSYNC_SOURCE_APP);
            }
        };
    

    为调用线程创建一个Choreographer实例,调用线程必须具备消息循环功能,因为ViewRootImpl对象的构造是在应用程序进程的UI主线程中执行的,因此创建的Choreographer对象将使用UI线程消息队列。
    构造函数的第二个参数是VSYNC_SOURCE_APP,看一下它的定义,表示发出同步信号app

        /**
         * When retrieving vsync events, this specifies that the vsync event should happen at the normal
         * vsync-app tick.
         * <p>
         * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
         */
        public static final int VSYNC_SOURCE_APP = 0;
    
        private Choreographer(Looper looper, int vsyncSource) {
            mLooper = looper;
            //创建消息处理Handler
            mHandler = new FrameHandler(looper);
        //如果系统使用了Vsync机制,则注册一个FrameDisplayEventReceiver接收器
            mDisplayEventReceiver = USE_VSYNC
                    ? new FrameDisplayEventReceiver(looper, vsyncSource)
                    : null;
            mLastFrameTimeNanos = Long.MIN_VALUE;
        //屏幕刷新周期
            mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
            //创建回调数组
            mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
          //初始化数组
            for (int i = 0; i <= CALLBACK_LAST; i++) {
                mCallbackQueues[i] = new CallbackQueue();
            }
        }
    

    变量USE_VSYNC用于表示系统是否是用了Vsync同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的。如果系统使用了Vsync同步机制,则创建一个FrameDisplayEventReceiver对象用于请求并接收Vsync事件,最后Choreographer创建了一个大小为4的CallbackQueue队列数组,用于保存不同类型的Callback。
    当界面需要重绘时,都会调用到ViewRootImp类的scheduleTraversals()方法,这里的实现也比较简单,代码如下:

    
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                if (ViewDebugManager.DEBUG_SCHEDULETRAVERSALS) {
                    Log.v(mTag, "scheduleTraversals: mTraversalBarrier = " + mTraversalBarrier
                            + ",this = " + this, new Throwable("scheduleTraversals"));
                }
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

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

        public void postCallback(int callbackType, Runnable action, Object token) {
            postCallbackDelayed(callbackType, action, token, 0);
        }
    
        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);
        }
    
    private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
            if (DEBUG_FRAMES) {
                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);
                }
            }
        }
    

    此处获取当前时间,然后加上要延迟的时间,作为当前Callback的时间点,以这个时间点作为标准,把Callback对象添加到mCallbackQueues[callbackType]队列当中,这块的逻辑和Looper、MessageQueue、Handler中添加Message的逻辑很相似,大家可以对比学习。然后判断dueTime <= now,也就是执行时间到会执行scheduleFrameLocked。否则执行else分支,往当前的队列中添加一个Message,那么通过Handler机制就会进行处理,此处的mHandler是一个FrameHandler对象,我们来看一下FrameHandler的代码:

        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;
                }
            }
        }
    

    这里的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()方法的实现

        void doScheduleCallback(int callbackType) {
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    final long now = SystemClock.uptimeMillis();
                    if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                        scheduleFrameLocked(now);
                    }
                }
            }
        }
    

    mFrameScheduled和ViewRootImpl的scheduleTraversals()方法中的变量mTraversalScheduled作用是一样的,也是判断当前是否正在执行添加,然后调用(mCallbackQueues[callbackType].hasDueCallbacksLocked(now))判断是否已处理过Callback事务,该方法的判断也很简单,(mHead != null && mHead.dueTime <= now),如果当前队列头不为空,并且队列头元素的时间点小于当前的时间点,那就说明是之前添加的,则需要对它进行处理;相反,如果队列头为空或者添加的时间点大于当前的时间点,也就是要延迟处理,则不需要任何操作。条件符合的话,就调用scheduleFrameLocked(now)进一步处理,我们来看一下scheduleFrameLocked()方法的实现:

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            //检查是否使用了Vsync机制
            if (USE_VSYNC) {
                //如果当前线程具备消息循环,则直接请求VSync信号
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {//如果当前线程不具备消息循环,则通过主线程请求VSync信号
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else { //如果系统没有使用VSync机制,则使用异步消息延时执行屏幕刷新
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }
    

    一开始就把mFrameScheduled赋值为true,表示事务开始执行了,那么上面doScheduleCallback()方法当中的代码此该就不会再执行了。在该函数中考虑了两种情况,一种是系统没有使用Vsync机制,在这种情况下,首先根据屏幕刷新频率计算下一次刷新时间,通过异步消息方式延时执行doFrame()函数实现屏幕刷新。如果系统使用了Vsync机制,并且当前线程具备消息循环,则直接请求Vsync信号,否则就通过主线程来请求Vsync信号。FrameDisplayEventReceiver对象用于请求并接收Vsync信号,当Vsync信号到来时,系统会自动调用其onVsync()函数,在该回调函数中执行doFrame()实现屏幕刷新。


    20140630191939812.png

    看到这里,是不是感觉逻辑有点多了,开始乱了,转来转去的,系统到底要干啥?呵呵,我们暂停下来梳理一下,系统做了这么多事情最终的目的就是在下一次Vsync信号到来的时候,将Choreographer当中的四个队列中的事务执行起来,这些事务是应用层ViewRootImpl在scheduleTraversals()方法中添加进去的,在Choreographer当中,我们要先将外边传进来的Callback放入队列,然后就要去请求Vsync信号,因为Vsync信号是定时产生的,你不请求,它就不会理你,当然你收不到回调,也就不知道啥时候通知ViewRootImpl执行View的measure、layout、draw了,这样说一下,大家清楚我们要干什么了吗?我第一次看Choreographer类的代码时候,看了半天,也是乱了,所以这里大概理一下。

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

    void doFrame(long frameTimeNanos, int frame) {
            final long startNanos;
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    return; // no work to do
                }
                long intendedFrameTimeNanos = frameTimeNanos;
                //保存起始时间
                startNanos = System.nanoTime();
                //由于Vsync事件处理采用的是异步方式,因此这里计算消息发送与函数调用开始之间所花费的时间
                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;
                    frameTimeNanos = startNanos - lastFrameOffset;
                }
    
            //如果frameTimeNanos小于一个屏幕刷新周期,则重新请求VSync信号
                if (frameTimeNanos < mLastFrameTimeNanos) {
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                + "previously skipped frame.  Waiting for next vsync.");
                    }
                    scheduleVsyncLocked();
                    return;
                }
                //Contains information about the current frame for jank-tracking,
                //mainly timings of key events along with a bit of metadata about
                //view tree state
                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
                mFrameScheduled = false;
                mLastFrameTimeNanos = frameTimeNanos;
            }
    
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
                AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
            //分别回调CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT事件,这里是四个队列,8.1上新增了CALLBACK_COMMIT
                mFrameInfo.markInputHandlingStart();
                doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    
                mFrameInfo.markAnimationsStart();
                doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    
                mFrameInfo.markPerformTraversalsStart();
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
                doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
            } finally {
                AnimationUtils.unlockAnimationClock();
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    
            /// M: PerfFrameInfo Mechanism
            mFrameInfo.markDoFrameEnd();
    
            if (DEBUG_FRAMES) {
                final long endNanos = System.nanoTime();
                Log.d(TAG, "Frame " + frame + ": Finished, took "
                        + (endNanos - startNanos) * 0.000001f + " ms, latency "
                        + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
            }
        }
    

    可以看到,平时我们通过Systrace进行性能分析的时候doFrame的Log就是在这里打出来的


    20140630192141906.png

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


    20140630192252750.png
    在Choreographer中定义了四个CallbackQueue队列,用数组mCallbackQueues表示,用于分别保存CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT这四种类型的Callback,当调用Choreographer类的postCallback()函数时,就是往指定类型的CallbackQueue队列中通过addCallbackLocked()函数添加一个CallbackRecord项:首先构造一个CallbackRecord对象,然后按时间先后顺序插入到CallbackQueue链表中。从代码注释中,我们可以知道CALLBACK_INPUT是指输入回调,该回调优先级最高,首先得到执行,而CALLBACK_TRAVERSAL是指处理布局和绘图的回调,只有在所有异步消息都执行完后才得到执行,CALLBACK_ANIMATION是指动画回调,比CALLBACK_TRAVERSAL优先执行,CALLBACK_COMMIT是绘制完成后的一些操作,在CALLBACK_TRAVERSAL之后执行。输入事件放在第一,也是为了能尽快响应用户的操作,但是即使这样,Android的流畅性还是不如IOS从doFrame()函数中的doCallbacks调用就能印证这点。
    当Vsync事件到来时,顺序执行CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL和CALLBACK_COMMIT对应CallbackQueue队列中注册的回调。
    void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                final long now = System.nanoTime();
    
            //从指定类型的CallbackQueue队列中查找执行时间到的CallbackRecord
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
    
                // Update the frame time if necessary when committing the frame.
                // We only update the frame time if we are more than 2 frames late reaching
                // the commit phase.  This ensures that the frame time which is observed by the
                // callbacks will always increase from one frame to the next and never repeat.
                // We never want the next frame's starting frame time to end up being less than
                // or equal to the previous frame's commit frame time.  Keep in mind that the
                // next frame has most likely already been scheduled by now so we play it
                // safe by ensuring the commit time is always at least one frame behind.
                if (callbackType == Choreographer.CALLBACK_COMMIT) {
                    final long jitterNanos = now - frameTimeNanos;
                    Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                    if (jitterNanos >= 2 * mFrameIntervalNanos) {
                        final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                                + mFrameIntervalNanos;
                        if (DEBUG_JANK) {
                            Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                    + " ms which is more than twice the frame interval of "
                                    + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                    + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                                    + " ms in the past.");
                            mDebugPrintNextFrameTimeDelta = true;
                        }
                        frameTimeNanos = now - lastFrameOffset;
                        mLastFrameTimeNanos = frameTimeNanos;
                    }
                }
            }
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            //由于CallbackQueues是按时间先后顺序排序的,因此遍历执行所有时间到的CallbackRecor
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG_FRAMES) {
                        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);
                }
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    

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

        private static final class CallbackRecord {
            public CallbackRecord next;
            public long dueTime;
            public Object action; // Runnable or FrameCallback
            public Object token;
    
            public void run(long frameTimeNanos) {
                if (token == FRAME_CALLBACK_TOKEN) {
                    ((FrameCallback)action).doFrame(frameTimeNanos);
                } else {
                    ((Runnable)action).run();
                }
            }
        }
    

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

    20140630192623921.png
    Vsync请求过程
    我们知道在Choreographer构造函数中,构造了一个FrameDisplayEventReceiver对象,用于请求并接收Vsync信号
    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable {
            private boolean mHavePendingVsync;
            private long mTimestampNanos;
            private int mFrame;
    
            public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
                super(looper, vsyncSource);
            }
    
            @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 / TimeUtils.NANOS_PER_MS);
            }
    
            @Override
            public void run() {
                mHavePendingVsync = false;
                doFrame(mTimestampNanos, mFrame);
            }
        }
    

    我们可以看到这里的mTimestampNanos时间定义都是纳秒级别的,因为Vsync信号是用来同步屏幕刷新频率的,所以对时间的要求非常高,才采用了纳秒级别的,mDisplayEventReceiver类变量是在Choreographer的构造方法中赋值的,我们继续来看它的scheduleVsync()方法的实现,因为FrameDisplayEventReceiver类是继承DisplayEventReceiver的,而它没用对scheduleVsync()方法重写,所以是调用父类的

    private void scheduleVsyncLocked() {
        //申请Vsync信号
        mDisplayEventReceiver.scheduleVsync();
    }
    

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

        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);
            }
        }
    

    它的实现很简单,判断描述符mReceiverPtr是否合法,如果非法就打印日志,什么也不作了,合法的话,就继续调用native方法nativeScheduleVsync(mReceiverPtr)来请求Vsync信号。nativeScheduleVsync()方法实现在android_view_DisplayEventReceiver.cpp当中,是通过定义JNINativeMethod gMethods[]来定义方法调用指针的,因为此类的代码不多,这里就全部贴出来,方便大家查看:

    namespace android {
    
    static struct {
        jclass clazz;
    
        jmethodID dispatchVsync;
        jmethodID dispatchHotplug;
    } gDisplayEventReceiverClassInfo;
    
    
    class NativeDisplayEventReceiver : public DisplayEventDispatcher {
    public:
        NativeDisplayEventReceiver(JNIEnv* env,
                jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource);
    
        void dispose();
    
    protected:
        virtual ~NativeDisplayEventReceiver();
    
    private:
        jobject mReceiverWeakGlobal;
        sp<MessageQueue> mMessageQueue;
        DisplayEventReceiver mReceiver;
    
        virtual void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
        virtual void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
    };
    
    
    NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
            jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource) :
            DisplayEventDispatcher(messageQueue->getLooper(),
                    static_cast<ISurfaceComposer::VsyncSource>(vsyncSource)),
            mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
            mMessageQueue(messageQueue) {
        ALOGV("receiver %p ~ Initializing display event receiver.", this);
    }
    
    NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        env->DeleteGlobalRef(mReceiverWeakGlobal);
        ALOGV("receiver %p ~ dtor display event receiver.", this);
    }
    
    void NativeDisplayEventReceiver::dispose() {
        ALOGV("receiver %p ~ Disposing display event receiver.", this);
        DisplayEventDispatcher::dispose();
    }
    
    void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
    
        ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
        if (receiverObj.get()) {
            ALOGV("receiver %p ~ Invoking vsync handler.", this);
            env->CallVoidMethod(receiverObj.get(),
                    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();
    
        ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
        if (receiverObj.get()) {
            ALOGV("receiver %p ~ Invoking hotplug handler.", this);
            env->CallVoidMethod(receiverObj.get(),
                    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 receiverWeak,
            jobject messageQueueObj, jint vsyncSource) {
        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,
                receiverWeak, messageQueue, vsyncSource);
        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) {
        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 const JNINativeMethod gMethods[] = {
        /* name, signature, funcPtr */
        { "nativeInit",
                "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;I)J",
                (void*)nativeInit },
        { "nativeDispose",
                "(J)V",
                (void*)nativeDispose },
        // @FastNative
        { "nativeScheduleVsync", "(J)V",
                (void*)nativeScheduleVsync }
    };
    
    int register_android_view_DisplayEventReceiver(JNIEnv* env) {
        int res = RegisterMethodsOrDie(env, "android/view/DisplayEventReceiver", gMethods,
                                       NELEM(gMethods));
    
        jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
        gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
    
        gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
                gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JII)V");
        gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
                gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JIZ)V");
    
        return res;
    }
    
    } // namespace android
    
    

    我们来看一下nativeScheduleVsync方法的定义,{ "nativeScheduleVsync", "(J)V", (void*)nativeScheduleVsync },这里需要说明一下,java方法和JNI方法存在着对应关系,"(J)V"括号里边的表示该方法的入参,括号外边的表示返回值J表示long,而返回值V表示Void。好了,我们继续看这个方法的实现,它将java层传进来的描述符强制转换为NativeDisplayEventReceiver对象,这样的处理在JNI当中是非常多见的,大家要熟悉。然后调用它的scheduleVsync()方法,最后根据返回值判断当前请求Vsync信号是否成功,如果status非0,则抛出RuntimeException异常。很明显,我们从这都可以猜出,正常情况下,返回的status应该为0了。

     我们继续来看NativeDisplayEventReceiver::scheduleVsync()方法的处理逻辑。
    
    status_t DisplayEventDispatcher::scheduleVsync() {
        if (!mWaitingForVsync) {
            ALOGV("dispatcher %p ~ Scheduling vsync.", this);
    
            // Drain all pending events.
            nsecs_t vsyncTimestamp;
            int32_t vsyncDisplayId;
            uint32_t vsyncCount;
            if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
                ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
                        this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
            }
    
            status_t status = mReceiver.requestNextVsync();
            if (status) {
                ALOGW("Failed to request next vsync, status=%d", status);
                return status;
            }
    
            mWaitingForVsync = true;
        }
        return OK;
    }
    

    首先检查mWaitingForVsync,如果当前正在请求Vsync信号,则就不需要重复请求了,只有在当前未请求的时候,才需要发出新的请求,然后调用processPendingEvents()将当前队列中还存在receiver处理掉.

    bool DisplayEventDispatcher::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("dispatcher %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("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
                   break;
               }
           }
       }
       if (n < 0) {
           ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
       }
       return gotVsync;
    }
    

    因此方法与我们的流程不相关,这里就不展开了,大致是使用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()方法的实现.

    status_t DisplayEventReceiver::requestNextVsync() {
        if (mEventConnection != NULL) {
            mEventConnection->requestNextVsync();
            return NO_ERROR;
        }
        return NO_INIT;
    }
    

    requestNextVsync()方法中直接调用mEventConnection->requestNextVsync()来请求Vsync信号,mEventConnection对象是在DisplayEventReceiver类的构造函数中创建的,mEventConnection = sf->createDisplayEventConnection(),sf就是SurfaceFlinger对象,SurfaceFlinger类的createDisplayEventConnection()实现也非常简单,就是调用mEventThread->createEventConnection()

    sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
              ISurfaceComposer::VsyncSource vsyncSource) {
          if (vsyncSource == eVsyncSourceSurfaceFlinger) {
              return mSFEventThread->createEventConnection();
          } else {
              return 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()方法了。
    onVsync()方法中以this为对象,向mHandler中添加了一个消息,消息处理的时候,就会调用它的run()方法了。run方法中直接调用doFrame()来进行处理。这个方法具体已经在上面分析过了。下面通过图来梳理Vsync请求的调用流程:

    20140630193116703.png

    这里就是将每种类型的事件队列中的元素取出来,通过for循环一一调用他们的run()方法了,调用完成后,将队列中的Callback回收掉。而这里的CallbackRecord对象就是我们在ViewRootImpl类当中添加的InvalidateOnAnimationRunnable、mConsumedBatchedInputRunnable、mTraversalRunnable这三类对象了,那么回到View的流程中,收到Vsync信号后,就会回调mTraversalRunnable的run()方法,再次发起一次measure、layout、draw流程,那么也就和Vsync信号对接上了。

    相关文章

      网友评论

          本文标题:Android8.1 Choreographer机制与源码分析

          本文链接:https://www.haomeiwen.com/subject/cbcjuftx.html