美文网首页
Android View的绘制机制

Android View的绘制机制

作者: AntCoding | 来源:发表于2019-11-27 15:17 被阅读0次

    大家好!我 胡汉三 又回来啦!由于之前的一段时间公司的项目比较忙碌,再加上我又全身心的投入到了 PMP 考证队伍中,导致博客停更了好久。
    不得不说考取 PMP证书,真的需要付出很大的精力;首先是因为心疼钱,因为考一次就得付出3900大洋,着实心疼啊!幸好付出和收获成了正比,顺利的拿到了 5A,结束了考试之旅。
    好了废话说完了,我们接下来就来上干货啦!

    View的绘制机制,究竟是怎么回事?

    源码啊!源码,你能否一股脑的钻进我的脑子啊!这样我就不用翻你千百遍,一回头你还你我还我了=_=!!

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
            ...
            //初始化WindowManageService 即初始化WMS
            WindowManagerGlobal.initialize();
            //创建Activity,并执行onCreate生命周期方法
            Activity a = performLaunchActivity(r, customIntent);
             
            if (a != null) { //证明Activity被正常启动
                r.createdConfig = new Configuration(mConfiguration);
                reportSizeConfigurations(r);
                Bundle oldState = r.state;
                handleResumeActivity(r.token, false, r.isForward,
                        !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
    
                if (!r.activity.mFinished && r.startsNotResumed) {
                    
                    performPauseActivityIfNeeded(r, reason);
                    ...
                }
            } else {
                //若执行出错告诉ActivityManager,结束当前Activity
                try {
                    ActivityManager.getService()
                        .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                                Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }
        }
    

    小结

      1. 初始化WindowManagerService
      1. 生成Activity,并执行Activity$onCreate(...)
      1. Activity被正常启动 则 执行 Activity$onResume(...)
      1. Activity未能正常启动 则 结束当前Activity即通过AMS执行finishActivity(...)

    分块

    • [ 1 ] ActivityThread $performLaunchActivity(...)是整个Activity创建的入口
    • [ 2 ] 在ActivityThread $handleResumeActivity(...)方法执行过程中传递给View类中常量mParent的属性值类型

    [ 1 ] ActivityThread $performLaunchActivity(...)

    ActivityThread.java

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            ...
            Activity activity = null;
              //通过类加载的方式创建Activity
            try {
                java.lang.ClassLoader cl = appContext.getClassLoader();
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
                StrictMode.incrementExpectedActivityCount(activity.getClass());
                r.intent.setExtrasClassLoader(cl);
                r.intent.prepareToEnterProcess();
                if (r.state != null) {
                    r.state.setClassLoader(cl);
                }
            } catch (Exception e) {
                ...
            }
    
            try {
              ...
               // [1.1] 通过Instrumentation调取Activity的onCretae
              if (r.isPersistable()) {
                   mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
               } else {
                   mInstrumentation.callActivityOnCreate(activity, r.state);
               }
               ...
            } catch (SuperNotCalledException e) {
                throw e;
    
            } catch (Exception e) {
                ...
            }
            return activity;
        }
    

    [ 1.1 ] Instrumentation$callActivityOnCreate

    Instrumentation.java

     public void callActivityOnCreate(Activity activity, Bundle icicle) {
            prePerformCreate(activity);
            //至此进入到`Activity$onCreate(...)`
            activity.performCreate(icicle);
            postPerformCreate(activity);
        }
    

    至此进入到Activity$onCreate(...)并在onCreate(...)中调用setContentView(int layoutResID)进行View的绘制,至此我们才开始正式流程:

    [ 1.2 ] Activity$setContentView(int layoutResID)

    Activity.java

     public void setContentView(@LayoutRes int layoutResID) {
            //[1.3] 获取Window对象,调取其setContentView设置视图
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    

    [ 1.3 ] PhoneWindow$setContentView(int layoutResID)

    PhoneWindow.java 由于Window是一个抽象类,而PhoneWindow是它唯一儿子,所谓 父债子还 嘛 !所以找它没错的

    @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null) {
               //安装DecorView
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
               ...
            } else {
                //解析Layout XML文件
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
           //请求视图更新
            mContentParent.requestApplyInsets();
            ...
        }
    

    小结

      1. mContentParentNull,表示当前视图无盛放的容器,即需要安装顶级视图DecorView
      1. 解析XML布局文件
      1. 请求视图更新操作

    [ 1.4 ] View$requestApplyInsets()

    View.java

        /**
         * The parent this view is attached to.
         * {@hide}
         *
         * @see #getParent()
         */
        protected ViewParent mParent;
        //为mParent变量赋值
        void assignParent(ViewParent parent) {
            if (mParent == null) {
                mParent = parent;
            } else if (parent == null) {
                mParent = null;
            } else {
                throw new RuntimeException("view " + this + " being added, but"
                        + " it already has a parent");
            }
        }
       /*
        * 这是重点:
        *        判定mParent是否为Null
        *        不为Null时,调取mParent的requestFitSystemWindows()
        */
        @Deprecated
        public void requestFitSystemWindows() {
            if (mParent != null) {
                mParent.requestFitSystemWindows();
            }
        }
    
       /**
         * 在PhoneWindow中调取的此方法,进行视图更新
         * 此方法无任何操作,而是直接调取了requestFitSystemWindows()
         */
        public void requestApplyInsets() {
            requestFitSystemWindows();
        }
    

    小结

      1. PhoneWindow中调取了View$requestApplyInsets(),由上述代码可知在此方法中没有做任何操作,而是直接调取了View$requestFitSystemWindows()
      1. 而在View$requestFitSystemWindows()方法中它判定了mParent在不为Null的情况下,调取了mParent$requestFitSystemWindows()
      1. 我们发现assignParent(ViewParent parent)是为mParent赋值的地方,而mParent的类型为ViewParent,但可惜的是ViewParent是一个接口,并没有实现代码
    public interface ViewParent {}
    

    那这事就郁闷了,无法确定mParent真实的类型,那么就无法找到 ViewParent$requestFitSystemWindows()真实实现的地方。那么我们就得再次回归源码了,继续 Read The Fucking Source Code . . . .

    [ 2 ] ActivityThread$handleResumeActivity(...)

    Activity被正常创建粗来了,也走完了ActivityonCreate生命周期,那么接下来就该走onResume生命周期了,我来看看在这里我们能不能找到想要的答案~

     final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ...
            r = performResumeActivity(token, clearHide, reason);
            if (r != null) {
                final Activity a = r.activity;
                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;
                            //重点来啦:这里将DecorView添加到WindowManager中
                            wm.addView(decor, l);
                        } else {
                            a.onWindowAttributesChanged(l);
                        }
                    }
                } else if (!willBeVisible) {
                    if (localLOGV) Slog.v(
                        TAG, "Launch " + r + " mStartedActivity set");
                    r.hideForNow = true;
                }
                ...
    
            } else {
                ...
            }
        }
    

    [ 2.1 ]WinodwManagerImpl$addView(...)

    WindowManager是一个接口,而他的实现类是WindowManagerImpl,所以我们就 单刀直入啦!

    WindowManagerImpl.java

        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
      @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            //不做过多论述,调取WindowManagerGlobal$addView(...)
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
    

    [ 2.2 ] WindowManagerGlobal$addView(...)

    WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
    
                ....
               //创建ViewRootImpl对象
                root = new ViewRootImpl(view.getContext(), display);
                view.setLayoutParams(wparams);
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
                try {
                    //重点:
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    ...
                }
            }
        }
    

    [ 2.3 ] ViewRootImpl$setView(...)

    ViewRootImpl.java

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
                    ...
                    //我们要的就是它,原来是在这里为 View中mParent赋值的,
                   //为什么是 this 呢? 难道。。。
                    view.assignParent(this);
                   ...
                }
            }
        }
    

    看看的类描述

    public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {...}
    

    原来如此,ViewRootImpl原来也是ViewParent接口的实现类之一啊~
    那么至此我们就搞清楚ViewmParent的具体类型了,就是ViewRootImpl类型!

    是时候解答疑惑了,让我回过头看看[ 1.3 ]中的代码
       /*
        * 这是重点:
        *        判定mParent是否为Null
        *        不为Null时,调取mParent的requestFitSystemWindows()
        */
        @Deprecated
        public void requestFitSystemWindows() {
            if (mParent != null) {
                mParent.requestFitSystemWindows();
            }
        }
    

    我们已经确认当前的mParent是类型了,那么我们直接去ViewRootImpl中寻找这个方法

    [ 1.4 ] ViewRootImpl$requestFitSystemWindows()

    ViewRootImpl.java

        @Override
        public void requestFitSystemWindows() {
             //检查线程
            checkThread();
            mApplyInsetsRequested = true;
            //开始遍历view
            scheduleTraversals();
        }
    
        void scheduleTraversals() {
            //当绘制任务执行期间,不允许有其他绘制任务执行
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                //向Looper的消息队列发布一个同步障碍
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
               //重点来了: post一个消息,执行mTraversalRunnable中任务
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    [ 1.5 ] mTraversalRunnable

     //它实现了Runnable接口
      final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                //执行遍历任务
                doTraversal();
            }
        }
       final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
       //执行遍历任务
       void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
                //重点中的最后一击
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }
    

    当我们看到performTraversals(),我们看到了黎明的曙光。。。没错就是它~~

    [ 1.6 ] performTraversals()

     private void performTraversals() {
      ...
      //测量
      performMeasure();
      ...
      //布局
      performLayout();
      ...
      //绘制
      perfromDraw
      ...
    }
    
    执行完此方法后,我们就完成了对View测量 ==> 布局 ==> 绘制 ,也就完成了我们的整个流程的剖析!!

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

    问题

    • 经过上述文章论述,我们知道了DecorView.mParentViewRootImpl类型,那么DecorView儿子的mParent是谁呢?它儿子的儿子的mParent又是谁呢?总不能都是ViewRootImpl类型吧!!

    答案肯定是否定的。
    mParent的类型永远同当前View的父级一个类型。

    来源码走一圈,让我们见真知!!

    ViewGroup.java

      public void addView(View child, int index, LayoutParams params) {
            ...
            //设置新LayoutParams时,addViewInner()将调用child.requestLayout()
           //因此,我们之前调用requestLayout()在我们自己,以便孩子的请求
            requestLayout();
            invalidate(true);
             //添加内部View
            addViewInner(child, index, params, false);
        }
    
      private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) {
            ...
            // tell our children
            if (preventRequestLayout) {
                child.assignParent(this);
            } else {
                child.mParent = this;
            }
            ...
        }
    

    通过以上的分析,我们表述已经很简明了,父View将自己赋予了子ViewmParent

    那么我可以清晰的总结一下:

    • DecorViewmParent的类型是ViewRootImpl
    • 其他ViewmParent的类型是Ta的父View,即 Ta的老子!! 哈哈

    That's all ! Thanks Everybody!

    相关文章

      网友评论

          本文标题:Android View的绘制机制

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