美文网首页安卓Android开发Android开发经验谈
最全的View绘制流程(上)— Window、DecorView

最全的View绘制流程(上)— Window、DecorView

作者: Geekholt | 来源:发表于2019-10-06 22:43 被阅读0次

    如需转载请评论或简信,并注明出处,未经允许不得转载

    目录

    前言

    对于接触Android开发不久的同学来说,要写一个页面,我们大多数时候都是先创建一个layout.xml布局文件,在布局文件中进行页面搭建,然后通过ActivitysentContentView()将布局文件设置到Activity中,这样Android系统就自动帮我们绘制了这个页面。我们知道,在Android中,一个页面是由一个个View组合而成的,那我们有没有想过,Android中View的绘制流程是怎么样的呢?本文将分为上下两部分,上部分主要讲ActivityWindowWindowManagerViewRoot等相关概念及互相之间的联系,了解这些有助于我们对View的绘制流程有一个更系统的认识,下部分会详细介绍view的measurelayoutdraw的过程

    Activity、Window、DecorView的关系.png

    Window和DecorView的创建

    我们先从我们比较熟悉的activity.setContentView()说起,查看这个方法的源码

    private Window mWindow;
    
    public void setContentView(@LayoutRes int layoutResID) {
        //调用window对象的setContentView()
        getWindow().setContentView(layoutResID);
        //创建ActionBar
        initWindowDecorActionBar();
    }
    public Window getWindow() {
            return mWindow;
    }
    

    我们发现,ActivitysetContentView()实际上调用的是WindowsetContentView()

    mWindow什么时候被创建的呢?

    Activity.java

    //只截取部分主要代码
    final void attach(...) {
            attachBaseContext(context);
            mFragments.attachHost(null /*parent*/);
                //创建PhoneWindow对象
            mWindow = new PhoneWindow(this, window, activityConfigCallback);
            mWindow.setWindowControllerCallback(this);
            mWindow.setCallback(this);
            mWindow.setOnWindowDismissedCallback(this);
            mWindow.getLayoutInflater().setPrivateFactory(this);
            if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
                mWindow.setSoftInputMode(info.softInputMode);
            }
            if (info.uiOptions != 0) {
                mWindow.setUiOptions(info.uiOptions);
            }
            ...
            //通过Context.getSystemService(Context.WINDOW_SERVICE)的方式获取WindowManager的实例
            //给window设置windowManger
            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();
            mCurrentConfig = config;
    
            mWindow.setColorMode(info.colorMode);
        }
    

    可以看出,mWindow是在Activityattach()中被创建的,它实际上是一个PhoneWindow对象,PhoneWindow是Android提供的Window唯一实现类。且系统会为每个Activity创建一个与之对应的Window

    创建Window后,还会通过Context.getSystemService(Context.WINDOW_SERVICE)的方式获取WindowManager的实例,并设置给WindowWindowManager是一个窗口管理类,稍后还会对WindowManager做更加深入的分析

    下面来看PhoneWindowsetContentView()

    @Override
    public void setContentView(int layoutResID) {
        //先判断mContentParent是否初始化
        if (mContentParent == null) {
            //如果没有初始化调用installDecor
            //当前activity第一次调用sentContentView
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            //如果Activity没有过度动画,多次调用sentContentView会走这里
            //移除mContentParent所有内部view
            mContentParent.removeAllViews();
        }
    
        //判断是否使用了Activity的过度动画
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            //设置动画场景
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //将资源文件通过LayoutInflater对象装换为View树
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            //这个方法在Activity是一个空实现
            //说明在Activity的布局改动时 (setContentView或者addContentView 方法执行完毕后会调用改方法)                  //所以各种View的findViewById方法什么的可以放在这里
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
    

    这里我们重点关注installDecor(),看看这个方法做了什么

      //只截取部分主要代码
      private void installDecor() {
          if (mDecor == null) {
              //如果mDecor为空则创建一个DecorView实例
              mDecor = generateDecor();  
              mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
              mDecor.setIsRootNamespace(true);
              if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                  mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
              }
          }
          if (mContentParent == null) {
                //如果mContentParent为空则通过generateLayout创建一个
              mContentParent = generateLayout(mDecor);
              ...
            }
      }
    
    

    installDecor() 主要干两件事情

    1. 如果mDecor没有初始化,则通过generateDecor()初始化
    2. 如果mContentParent 没有初始化,则通过generateLayout()初始化

    generateDecor()很简单,就是new一个DecorView实例

    protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        //创建new DecorView实例
        return new DecorView(context, featureId, this, getAttributes());
    }
    

    下面来分析,generateLayout()

        //只截取部分主要代码
        protected ViewGroup generateLayout(DecorView decor) {
           //根据当前style修饰相应样式
           TypedArray a = getWindowStyle();
           ...
           //一堆if判断,根据设置的主题样式来设置DecorView的风格
            if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
                requestFeature(FEATURE_NO_TITLE);
            } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
                // Don't allow an action bar if there is no title.
                requestFeature(FEATURE_ACTION_BAR);
            } else if(...){
                ...
            }  
    
           //加载窗口布局
           int layoutResource;
           int features = getLocalFeatures();
    
           //根据features选择不同的layoutResource(布局文件资源)
            if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
                layoutResource = R.layout.screen_swipe_dismiss;
            } else if(...){
                ...
            }
           //加载layoutResource
           View in = mLayoutInflater.inflate(layoutResource, null);
             //DecorView是FrameLayout,往DecorView中添加根布局View
           decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
           mContentRoot = (ViewGroup) in;
                    
             //这里获取的就是mContentParent
             //ID_ANDROID_CONTENT = com.android.internal.R.id.content;
           ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
           if (contentParent == null) {
               throw new RuntimeException("Window couldn't find content container view");
           }
           ...
           return contentParent;
       }
    
    

    根据设置的Window主题样式来设置DecorView的风格,接着为DecorView添加子View,而这里的子View则是上面提到的mContentParent,如果上面设置了FEATURE_NO_ACTIONBAR,那么DecorView就只有mContentParent一个子View。可以看我们文章一开始的那张图,图中就很好的阐述了DecorViewTitleViewmContentParent三者之间的关系

    所以这也就是为什么,在设置Activity属性的时候,比如requestWindowFeature(Window.FEATURE_NO_TITLE) 需要在setContentView()之前调用才会生效

    小结

    1. 一个Activity对应一个WindowWindow是在activity.attach()的时候被创建的
    2. Activity中调用setContentView()实际上是调用了WindowsetContentView()
    3. 调用setContentView()时,会去初始化DecorView以及其内部的TitleViewmContentParent,可以通过设置Window.FEATURE_NO_TITLE使得DecorView内部只存在mContentParent
    4. 我们在xml中定义的layout_activity.xml实际上是mContentParent的一个子View

    通过WindowManager管理Window

    上文中我们主要讲的是Window的创建、DecorView的创建以及DecorView的子View是如何被添加到DecorView中去的。每个Activity都有一个与之关联的Window,那如何管理Window呢?这时候就需要借助我们的WindowMananger类。WindowManager管理Window实际上就是在管理Window中的DecorView

    下面来看一下DecorViewWindowManager是如何关联起来的

    从Activity的启动开始分析(对Activity的启动过程感兴趣的还可以看Android应用进程的创建 — Activity的启动流程

    1. 使用代理模式启动到ActivityManagerService中执行
    2. 创建ActivityRecordmHistory记录中
    3. 通过socket通信到Zgote相关类创建process
    4. 通过ApplicatonThreadActivityManagerService建立通信
    5. ActivityManagerService通知ActiveThread启动Activity的创建
    6. ActivityThread创建Activity加入到mActivities中并开始调度Activity执行
    7. activityThread.handleLaunchActivity()

    上面就是Activity启动的大致流程,紧接上面第7步,从ActivityThread开始执行到DecorView被添加到WindowManager中的过程如下(代码这里就省略了,有兴趣的自己可以搜索关键代码看一下),这里重点关注handleResumeActivity()方法

    activityThread.handleLaunchActivity() —> activityThread.performLaunchActivity()—> activity.attach() —> activity.onCreate()—> activityThread.handleResumeActivity() —>activityThread.performResumeActivity() —> activity.onResume() —> windowManager.addView()

    //只截取部分主要代码
    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { 
        ...
        // 这里会调用到activity.onResume()方法
        ActivityClientRecord r = performResumeActivity(token, clearHide); 
    
        if (r != null) {
            final Activity a = r.activity;
    
            ...
            if (r.window == null && !a.mFinished && willBeVisible) {
                    // 获得Window对象
                r.window = r.activity.getWindow(); 
                // 获得Window中的DecorView对象
                View decor = r.window.getDecorView(); 
                decor.setVisibility(View.INVISIBLE);
                // 获得WindowManager对象
                //这个WindowManager就是在activity.attach()的时候和window一起创建的
                ViewManager wm = a.getWindowManager(); 
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    // 调用windowManager.addView方法
                    wm.addView(decor, l); 
                }
                ...
                if (!r.activity.mFinished && willBeVisible
                        && r.activity.mDecor != null && !r.hideForNow) {
                    r.activity.mVisibleFromServer = true;
                    mNumVisibleActivities++;
                    if (r.activity.mVisibleFromClient) {
                            //设置decorView可见
                        r.activity.makeVisible();
                    }
                }
            }
        }
    }
    

    通过调用windowManager.addView(decor, lp),这样一来,DecorViewWindowManager就建立了联系。WindowManager继承了ViewManger接口,但实际上其本身依然是一个接口,实现类是WindowManagerImpl

    //WindowManagerImpl implements WindowManager
    //WindowManager extends ViewManager
    public interface ViewManager
    {
            public void addView(View view, ViewGroup.LayoutParams params);
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);
        public void removeView(View view);
    }
    
    
    //只截取部分主要代码
    public final class WindowManagerImpl implements WindowManager {
        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
        private final Context mContext;
        private final Window mParentWindow;
    
        public WindowManagerImpl(Context context) {
            this(context, null);
        }
    
        private WindowManagerImpl(Context context, Window parentWindow) {
            mContext = context;
            mParentWindow = parentWindow;
        }
    
        public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
            return new WindowManagerImpl(mContext, parentWindow);
        }
    
        public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
            return new WindowManagerImpl(displayContext, mParentWindow);
        }
    
        //往Window中添加view
        @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
    
        //更新布局
        @Override
        public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.updateViewLayout(view, params);
        }
    
        //将删除View的消息发送到MessageQueue中,稍后删除
        @Override
        public void removeView(View view) {
            mGlobal.removeView(view, false);
        }
    
        //立刻删除Window中的view
        @Override
        public void removeViewImmediate(View view) {
            mGlobal.removeView(view, true);
        }
    }
    

    注意:同样实现了ViewManger接口的还有ViewGroup,我们知道ViewGroup也有addView方法,但是在ViewGroup中是将普通的view或者viewGroup作为Children加入,而在WindowManagerImpl是将DecorView作为根布局加入到PhoneWindow中去,所以两个方法的作用是截然不同的

    我们发现,WindowManagerImpl并没有直接实现操作View的相关方法,而是全部交给WindowManagerGlobalWindowManagerGlobal是一个单例类—即一个进程中最多仅有一个。创建WindowManagerGlobal对象的方式如下

    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }
    
    ViewManager、WindowManager、WIndowManagerImpl、WIndowManagerGlobal关系UML图

    深入分析WindowManagerGlobal

    分析WindowManangerGlobal实际上就是分析其内部的三个方法

    addView(View view, ViewGroup.LayoutParams params)

    该方法的主要作用是将decorView添加到viewRootImp中,通过viewRootImp对其内部的view进行管理

    public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
                //参数检查
                if (view == null) {
                throw new IllegalArgumentException("view must not be null");
            }
                ...
            //判断是否有父Window,从而调整当前窗口布局参数(layoutParams)
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {
                //有,调整title和token
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            } else {
                //无,应用开启了硬件加速的话,decorview就开启
                final Context context = view.getContext();
                if (context != null
                        && (context.getApplicationInfo().flags
                                & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                    wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
                }
            }
            ViewRootImpl root;
            View panelParentView = null;
            synchronized (mLock) {
                if (mSystemPropertyUpdater == null) {
                    //创建一个Runnable,用来遍历更新所有ViewRootImpl,更新系统参数
                    mSystemPropertyUpdater = new Runnable() {
                        @Override public void run() {
                            synchronized (mLock) {
                                for (int i = mRoots.size() - 1; i >= 0; --i) {
                                    mRoots.get(i).loadSystemProperties();
                                }
                            }
                        }
                    };
                    //添加到执行队列中
                    SystemProperties.addChangeCallback(mSystemPropertyUpdater);
                }
    
                //看需要add的view是否已经在mViews中
                //WindowManager不允许同一个View被添加两次
                int index = findViewLocked(view, false);
                if (index >= 0) {
                    // 如果被添加过,就看是否在死亡队列里也存在
                    if (mDyingViews.contains(view)) {
                        // 如果存在,就将这个已经存在的view对应的window移除
                        mRoots.get(index).doDie();
                    } else {
                        //否则,说明view已经被添加,不需要重新添加了
                        throw new IllegalStateException("View " + view
                                + " has already been added to the window manager.");
                    }
                }
                            // 如果属于子Window层级
                if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                        wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                    final int count = mViews.size();
                    //遍历viewRootImpl集合,看是否存在一个window的IBinder对象和需要添加的window的token一                                       //致,之后赋值引用
                    for (int i = 0; i < count; i++) {
                        if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                            panelParentView = mViews.get(i);
                        }
                    }
                }
                            //创建一个ViewRootImpl对象并保存在root变量中
                root = new ViewRootImpl(view.getContext(), display);
                            //给需要添加的view设置params,也就是decorView
                view.setLayoutParams(wparams);
                            //将decoView、布局参数以及新建的ViewRootImpl保存在三个集合中
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
                try {
                    //将decorView设置给ViewRootImpl
                    //ViewRootImpl向WMS添加新的窗口、申请Surface以及decorView在Surface上的重绘动作
                    //这才是真正意义上完成了窗口的添加操作
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                }
            }
        }
    
    • 父窗口修改新窗口的布局参数。可能修改的只有LayoutParams.tokenLayoutParams.mTitle两个属性。mTitle属性不必赘述,仅用于调试。而token属性则值得一提,每一个新窗口必须通过LayoutParams.tokenWMS出示相应的令牌才可以。在addView()函数中通过父窗口修改这个token属性的目的是为了减少开发者的负担。开发者不需要关心token到底应该被设置为什么值,只需将LayoutParams丢给一个WindowManager,剩下的事情就不用再关心了。父窗口修改token属性的原则是:如果新窗口的类型为子窗口(其类型大于等于LayoutParams.FIRST_SUB_WINDOW并小于等于LayoutParams.LAST_SUB_WINDOW),则LayoutParams.token所持有的令牌为其父窗口的ID(也就是IWindow.asBinder()的返回值)。否则LayoutParams.token将被修改为父窗口所属的Activity的ID(也就是在第4章中所介绍的AppToken),这对类型为TYPE_APPLICATION的新窗口来说非常重要。从这点来说,当且仅当新窗的类型为子窗口时addView()parentWindow参数才是真正意义上的父窗口。这类子窗口有上下文菜单、弹出式菜单以及游标等等,在WMS中,这些窗口对应的WindowState所保存的mAttachedWindow既是parentWindow所对应的WindowState。然而另外还有一些窗口,如对话框窗口,类型为TYPE_APPLICATION, 并不属于子窗口,但需要AppToken作为其令牌,为此parentWindow将自己的AppToken赋予了新窗口的的LayoutParams.token中。此时parentWindow便并不是严格意义上的父窗口了
    • 为新窗口创建一个ViewRootImpl对象。顾名思义,ViewRootImpl实现了一个控件树的根。它负责与WMS进行直接的通讯,负责管理Surface,负责触发控件的测量与布局,负责触发控件的绘制,同时也是输入事件的中转站。总之,ViewRootImpl是整个控件系统正常运转的动力所在,无疑是本章最关键的一个组件。
    • 将控件、布局参数以及新建的ViewRootImpl对象以相同的索引值添加到三个对应的集合mViewsmParams以及mRoots中,以供之后的查询之需。控件、布局参数以及ViewRootImpl三者共同组成了客户端的一个窗口。或者说,在控件系统中的窗口就是控件、布局参数与ViewRootImpl对象的一个三元组
    LayoutParams、View、ViewRootImp的关系.png

    updateViewLayout(View view, ViewGroup.LayoutParams params)

    该方法的主要作用是更新decorViewlayoutParams,如layoutParams.width从100变为了200,则需要将这个变化通知给WMS使其调整Surface的大小,并让窗口进行重绘

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
            //参数检查
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        //将layoutparams保存到decorView中
        view.setLayoutParams(wparams);
        synchronized (mLock) {
            // 获取decorView在三个集合中的索引
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            //更新layoutParams到集合中
            mParams.add(index, wparams);
            //调用viewRootImpl的setLayoutParams()使得新的layoutParams生效
            root.setLayoutParams(wparams, false);
        }
    }
    

    removeView(View view)

    该方法的作用是从3个集合中删除此Window所对应的元素,包括decorViewlayoutPrams以及viewRootImpl,并要求viewRootImplWMS中删除对应的Window,并释放一切需要回收的资源

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
      
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);//其内部会调用root.die(immediate)
            if (curView == view) {
                return;
            }
          
            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }
    

    要求viewRootImplWMS中删除窗口并释放资源的方法是调用viewRootImpl.die()函数。因此可以得出这样一个结论:viewRootImpl的生命从setView()开始,到die()结束

    通过ViewRootImp管理View

    ViewRootImpl实现了ViewParent接口,它是WindowManagerGlobal工作的实际实现者

    我们先来看setView()做了什么

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         synchronized (this) {
             if (mView == null) {
                //mView保存了decorView
                 mView = view;
                //mWindowAttributes保存了窗口所对应的LayoutParams
                 mWindowAttributes.copyFrom(attrs);     
                 attrs = mWindowAttributes;
                 ...
                         //请求UI开始绘制
                 requestLayout();   
                 //初始化mInputChannel
                 //InputChannel是窗口接受来自InputDispatcher的输入事件的管道
                 //注意,仅当窗口的属性inputFeatures不含有INPUT_FEATURE_NO_INPUT_CHANNEL时
                 //才会创建InputChannel,否则mInputChannel为空,从而导致此窗口无法接受任何输入事件
                 mInputChannel = new InputChannel();   
                 try {
                    //通知WindowManagerService添加一个窗口,注册一个事件监听管道
                    //用来监听 按键(KeyEvent)和触摸(MotionEvent)事件
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mInputChannel);
                    }
                    ...
            }
    }
    

    setView()内部的执行过程viewRootImp.setView() —> viewRootImp.requestLayout() —> viewRootImp.scheduleTraversals() —> viewRootImp.doTraversal() —> viewRootImp.performTraversals()—>进入view的绘制流程

    public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                //线程检查,这里就是判断更新UI的操作是否在主线程的方法
                checkThread();
                mLayoutRequested = true;
                scheduleTraversals();
            }
    }
    
    void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    
    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");
                }
            //开始view的绘制
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
    }
    

    总结

    本文主要帮助我们了解View的绘制开始之前经过了哪些流程,这有利于我们对整个View的绘制体系有一个更全面的认识,下面我们来具体分析View的
    最全的View绘制流程(下)— Measure、Layout、Draw

    相关文章

      网友评论

        本文标题:最全的View绘制流程(上)— Window、DecorView

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