Activity页面的绘制流程

一.引言

很多朋友都认为Activity的页面绘制是在Activity的onResume方法执行完成就被成功绘制成功了,认为这个时候我们就可以看到我们写的页面了。那真的是这样嘛?其实并不然,Activity的页面是在Activity的onResume方法执行完成后才会被绘制,且绘制完成的时长与我们主线程当时的状态和布局的层级以及CPU内存都是有一定的关系,这些也是我们分析Activity启动黑屏主要方向。并不要认为我们的Activity走完了onResume就能显示在用户眼前了。
好了废话不多少,我们从源码来分析一下,Activity的页面绘制究竟是在何时。

二.源码解析

- (1)ActivityThread.handleResumeActivity

熟悉Activity启动流程的朋友都知道,从AMS调Activity的onResume到App进程的主线程,有关Activity onResume生命周期的第一个方法就是handleResumeActivity,今天我们暂时不讨论Activity的启动流程,下次我会单独写一篇文章来详细说明Activity的启动流程。这篇文章我们就从handleResumeActivity这个方法开始我们今天的主题。下面我们看看它的源码。

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {

        ......
        //1.最后会调到Activity的onResume
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        
        ......

        final Activity a = r.activity;
        ......
        
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            
            2.获取WindowManagerImpl对象
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    
                    3.调用WindowManagerImpl的addView方法
                    wm.addView(decor, l);
                } 
                ......
            }
           ......
        }
      .......
    }

1.我们先来看看注释1
跟一下它的源码,代码只贴主要流程的,有兴趣的朋友可以自己去查看详细的。

  @VisibleForTesting
    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
            
         //省略部分代码
         .....
         .....
        final ActivityClientRecord r = mActivities.get(token);
         //省略部分代码
         .....
         .....
         //调用Activity的performResume方法
         r.activity.performResume(r.startsNotResumed, reason);


        return r;
    }




    final void performResume(boolean followedByPause, String reason) {
    

       //省略部分代码
       ....
       ....
       //调用Instrumentation的callActivityOnResume方法
        mInstrumentation.callActivityOnResume(this);
       //省略部分代码
       ....
       ....

    }
    
    
    public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        //调用Activity的onResume
        activity.onResume();
        
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i

上面我们跟了一下performResumeActivity的源码,发现最后调用了Activity的onResume,由此我们可以知道在注释一的时候,Activity的onResume方法其实已经执行完成了,但是我们的界面绘制还没有开始呢,印证了我们开头说的。

2.下面我们来看看注释2这个wm到底是个什么东西呢,看看它的具体实现类是什么。

   //Activity的getWindowManager方法
    public WindowManager getWindowManager() {
        return mWindowManager;
    }



  //mWindowManager是在Activity的attach方法中被创建的
  //这个方法是handleResumeActivity前执行的分析启动流程的时候我们会说到
   final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
       
        //调用setWindowManager方法
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        //直接调用Window的getWindowManager方法
        mWindowManager = mWindow.getWindowManager();

    }
   
    //Window的setWindowManager方法
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        .......
        .......
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        //调用WindowManagerImpl的createLocalWindowManager方法创建mWindowManager对象
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

   //Window的getWindowManager方法
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

    //WindowManagerImpl的createLocalWindowManager创建的是一个WindowManagerImpl对象
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

根据以上源码我们知道了注释2的vm其实就是WindowManagerImpl对象。注释1注释2这两个前菜已经给大家分析完了。下面我们进入主题Activity页面的绘制。

- (2)WindowManagerImpl.addView

通过前面的分析,我们可以知道注释3处其实就是调用了WindowManagerImpl的addView方法。下面我们来看看这个方法到底做了什么。

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //调用WindowManagerGlobal的addView方法
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

   //WindowManagerGlobal的addView
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            
        ......
        ......
        
        ViewRootImpl root;
        View panelParentView = null;
        
        .....
        .....
           
           1.创建ViewRootImpl对象
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                2.调用ViewRootImpl的setView方法
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

  

注释1是个很重要的点,这里创建了一个ViewRootImpl对象。

我们来解析一下这个ViewRootImpl里面需要关注的几个点。
我们先看看ViewRootImpl的构方法:

public ViewRootImpl(Context context, Display display) {
        //获取Session对象
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();
        //主线程
        mThread = Thread.currentThread();

       //创建Choreographer对象,这个对象很重要,可以把它理解为一个Handler
       //Android 16.6ms刷新一次页面它启到了主要作用
       //我们马上看看这个Choreographer是个什么
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

    }

ViewRootImpl的构造方法里面我们暂时值关注两件事
1.获取了Session对象这个对象是我们与WindowManagerService交互的桥梁。
2.创建了Choreographer对象。

我们现来看看这个Choreographer对象到底是什么,为什么它那么重要呢?

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


    // Thread local storage for the choreographer.
    private static final ThreadLocal sThreadInstance =
            new ThreadLocal() {
        @Override
        protected Choreographer initialValue() {
           //注意了这里其实就是主线程的Looper
           //ViewRootImpl对象就是在主线程创建的
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            //创建Choreographer对象
            Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = choreographer;
            }
            return choreographer;
        }
    };

    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        //前面我们说了正常情况瞎这个looper对象就是主线程的looper对象
        //所以通过这个Handler发送的消息都是在主线程处理的
        mHandler = new FrameHandler(looper);
        
        //创建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();
        }
        // b/68769804: For low FPS experiments.
        setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
    }




 private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }
       
        //这个的onVsync方法是什么时候调用的呢?
        //我们知道Android没16.6ms都会刷新一次屏幕,原理其实就是这个Vsync导致的
        //这个Vsync信号是从底层传递上来的
        //onVsync这个方法也是通过jni从底层调用上来,这个方法不会被java层调用
        //每16.6ms调用一次
        @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.

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //通过Handler把自身作为一个任务发送到主线程的消息队列去做处理
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            //当绘制任务被处理时调用doFrame方法
            doFrame(mTimestampNanos, mFrame);
        }
    }


 void doFrame(long frameTimeNanos, int frame) {
     .....
     .....
            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;
            }
            
            //执行这一次队列中的绘制任务
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

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

      ......
      ......

   }

 void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
         final long now = System.nanoTime();
           //根据callbackType取出mCallbackQueues的任务
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
        synchronized (mLock) {
   
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
           //for循环执行需要绘制的任务
            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);
            }
        } 
    }


上面我们简单的讲了ViewRootImpl的创建和Choreographer的创建。我们知道了每16.6ms底层都会通过jni调用成员变了mDisplayEventReceiver的onVsync方法,这个方法里会通过成员变量mHandler(与主线程Looper绑定的Handler),把自己作为一个任务发送到主线程去执行。
最后调用doFrame()方法来处理mCallbackQueues队列中的绘制任务。我们可以猜测最后我们Activity页面的绘制任务也会被添加到这个mCallbackQueues队列中。
下面我们再来看最后也是最重要的一步。

- (3)ViewRootImpl.setView

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;



                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //1.请求布局绘制
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //2.通过构造方法里创建的Session对象向WindowManangerService请求将我们创建好的Surface对象添加到                   屏幕上
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } 
                
            }
        }
    }

由于今天我们讲绘制流程,我们就主要来看看注释1做了写什么事。我们按照方法的调用流程来看看源码。

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            //检查是否是主线程要求更新UI
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //调用Choreographer的postCallback方法
            //mTraversalRunnable就是一个处理绘制的任务
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
           .....
        }
    }


    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

    void doTraversal() {
            ......
            ......
            performTraversals();
            ......
            ......
    }

    //performTraversals最后会调用performMeasure,performLayout,performDraw
    //来处理界面的测量,布局和绘制流程
     private void performTraversals() {
         
           ....
           .....
 
            //处理测量流程
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            
            .....
            .....
            
            //处理布局流程
            performLayout(lp, mWidth, mHeight);

            ...
            ...
            
            //处理绘制流程     
            performDraw();
  

    }


   //下面我们开始分析Choreographer postCallback流程
    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) {
        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {

        synchronized (mLock) {
            //这里小小的说明一下这里记录的是开机到现在的时间
            //不受设置时间的影响
            //如果要计算时间间隔最好用 SystemClock.uptimeMillis()
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            //根据当前时间点将任务加入任务队列
           //前面讲Choreographer有说明
           //最后这个队列里面存储的任务会在onVsync方法被调用的时候被执行
           //onVsync方法每16.6ms底层通过jni调用
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

             .....
             .....
        }
    }

三.总结

经过上面的分析,我相信大多数朋友应该对Activity页面的绘制流程有了一定的了解。有兴趣的朋友可以自己按照我上面的分析流程跟一下源码。
下面我们做个简单的总结。

Activity页面什么时候被绘制?
Activity的onResume方法执行完成之后,我们的Activity页面绘制还没有开始。当系统后续调用WindowManagerImpl的addView方法时,系统会创建一个ViewRootImpl对象,创建ViewRootImpl对象的时候,系统也会去创建一个Choreographer对象。这个Choreographer对象类似于一个Handler只不过它接收的底层传上来的Vsync事件。ViewRootImpl对象创建好了之后,会调用ViewRootImpl的setView方法,这个方法最后会把绘制任务添加到Choreographer的成员变量mCallbackQueues中。当底层每隔16.6ms调用一次onVsync方法时,会根据任务的时间存入的想要执行的时间来执行绘制任务。且绘制任务的执行是在主线程,就算我们的Activity的onResume被执行完成了,然后主线程出现了卡顿,我们Activity页面的绘制任务可能出现延迟,甚至不执行。

你可能感兴趣的:(Android,Java)