Android View第一次绘制

Posted danfengw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android View第一次绘制相关的知识,希望对你有一定的参考价值。

疑问

1 android 走生命周期的时候,View究竟是在哪个生命周期进行测量布局绘制的呢?
2 在该生命周期中通知View的绘制流程是怎样的?

解答

1 Android的resume周期开始绘制的,这也是我们为什么在onResume的时候获取不到view的狂高
2 下面来看下具体流程(view的绘制是通过ViewRootImp的scheduleTraversals方法,因此看下怎么从ActivityThread找到ViewRootImp的scheduleTraversals的调用的

直接来看ActivityThread.java 中的handleResumeActivity中的处理,代码如下

  if (r.window == null && !a.mFinished && willBeVisible) 
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            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;
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) 
                    impl.notifyChildRebuilt();
                
            
            if (a.mVisibleFromClient) 
                if (!a.mWindowAdded) 
                    a.mWindowAdded = true;
                    //wm被addView
                    wm.addView(decor, l);
                 else 
                    a.onWindowAttributesChanged(l);
                
            
         else if (!willBeVisible) 
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        

上面的wm是ViewManager的getWindow(),而Activity的getWindowManager()最终返回的对象是WindowManager

  public WindowManager getWindowManager() 
        return mWindowManager;
    

再来看Activity的mWindowManager 是何时创建的呢?请看下面attach方法中的代码,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());
        
        mWindowManager = mWindow.getWindowManager();

这时点击到setWindowManager方法(如下)查看发现,mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);,至此也就是addView方法最终的调用者,实际是WindowManagerImpl对象,因此我们需要去查看WindowManagerImpl的addView方法做了什么操作

  public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) 
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) 
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    

现在来看WindowManagerImpl的addView方法做了什么

 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    

WindowManagerImpl最终通过mGlobal对象进行的addView,而mGlobal是一个WindowManagerGlobal对象,WindowManagerGlobal中的addView方法中有如下代码,

     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 
            	//ViewRootImpl的setView方法
                root.setView(view, wparams, panelParentView, userId);
             catch (RuntimeException e) 
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) 
                    removeViewLocked(index, true);
                
                throw e;
            

ViewRootImpl的setView方法中调用了requestLayout();,与view.assignParent(this);,关于assignParent()需要关联其他代码,为了避免思路有分歧,这里不介绍它。继续看下面的requestLayout

  @Override
    public void requestLayout() 
        if (!mHandlingLayoutInLayoutRequest) 
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        
    

这里requestLayout 调用到scheduleTraversals()方法,scheduleTraversals方法中通过postSyncBarrier来插入一个消息屏障,其中又通过Choreographer对象发送mTraversalRunnable,

  @UnsupportedAppUsage
    void scheduleTraversals() 
        if (!mTraversalScheduled) 
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        
    

来看看看TraversalRunnable被调用的时候做了什么?调用了doTraversal()

 final class TraversalRunnable implements Runnable 
        @Override
        public void run() 
            doTraversal();
        
    
    void doTraversal() 
        if (mTraversalScheduled) 
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) 
                Debug.startMethodTracing("ViewAncestor");
            

            performTraversals();

            if (mProfile) 
                Debug.stopMethodTracing();
                mProfile = false;
            
        
    

之后在中可以看到调用了performLayout(lp, mWidth, mHeight);从而进一步调用如下代码:

 measureHierarchy(host, lp, mView.getContext().getResources(),
                            desiredWindowWidth, desiredWindowHeight);
                    mInLayout = true;
                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

通过上面的measureHierarchy和host.layout来进行后续的测量与布局。

小结

ActivityThread—》handleResumeActivity—》addView–》WindowManagerImp—》WindowManagerGlobal–》addView—》ViewRootImpl—》setView—》requestLayout()–》scheduleTraversals()–》doTraversal()----》performTraversals()—》测量与布局

流程经过的类:ActivityThread、WindowManagerImp、WindowManagerGlobal、ViewRootImpl

以上是关于Android View第一次绘制的主要内容,如果未能解决你的问题,请参考以下文章

android View的测量和绘制

Android面试收集录12 View测量布局及绘制原理

Android绘制源码分析(下)

Android view的测量及绘制

[译]Android view 测量布局和绘制的流程

反思|Android View机制设计与实现:测量流程