Android进阶之路------View的工作原理(1)初识ViewRoot与DecorView

ViewRoot 与 DecorView

了解View的工作原理之前,需要先了解ViewRoot与DecorView。

ViewRoot对应于ViewRootImpl类,它是链接WindowManager和DecorView的纽带,最终通过ViewRootImpl类中的performTraversals方法完成View的三大流程:measure,layout,draw。

那么ViewRoot是如何链接WindowManager和DecorView的,以及如何完成三大流程的绘制的?

ViewRoot链接WindowManager和DecorView

DecorView是PhoneWindow的内部类,当Activity初始化时,ActivityThread类会调用handleResumeActivity方法将顶层的DecorView添加到PhoneWindow窗口。
代码路径

     final void handleResumeActivity(IBinder token,
             boolean clearHide, boolean isForward, boolean reallyResume) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         
					....................
					
             if (r.window == null && !a.mFinished && willBeVisible) {
					//获取Activity的PhoneWindow
                 r.window = r.activity.getWindow();
					//获取PhoneWindow内部类DecorView
                 View decor = r.window.getDecorView();
                 decor.setVisibility(View.INVISIBLE);
					//获取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 (a.mVisibleFromClient) {
                     a.mWindowAdded = true;
                 if (a.mVisibleFromClient) {
                     a.mWindowAdded = true;
					//通过WindowManager将DecorView添加到当前Activity
                     wm.addView(decor, l);
                 }
                 
				..................
				
     }

由代码中可以看出,首先获取获取Activity的PhoneWindow,之后获取获取PhoneWindow内部类DecorView,最后获取WindowManager将DecorView添加到当前Activity。我们可以继续看一下WindowManagerImpl类中的 wm.addView(); 方法。

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

最终调用了WindowManagerGlobal.addView(frameworks\base\core\java\android\view)方法,代码如下:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            
        .............

        ViewRootImpl root;
        View panelParentView = null;
		synchronized (mLock) {
		
		......
		
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }
        try {
            root.setView(view, wparams, panelParentView);
        }
        
		.......

    }

其中ViewRootImpl类中的setView方法负责将刚才add进来的decorWindow添加到panelParentView中去。我们继续跟踪,进入到setView中看看发生了什么。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                .......
                               
                mAdded = true;
                ......
               //在添加到窗口管理器之前安排第一个布局,
               //以确保在从系统接收任何其他事件之前进行重新布局。.
                requestLayout();
                ......
            }
        }
    }

setView方法中通过 requestLayout(); 方法为添加的view安排布局。

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            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();
        }
    }

跟踪到 scheduleTraversals(); 方法中,通过 mTraversalRunnable进行回调。

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

其中performTraversals(); 方法就是View绘制流程的开始。

DecorView

上面介绍了ViewRoot如何将DecorView和WindowManager链接在一起,下面需要弄清楚我们链接的这个DecorView到底是什么。

setContentView(R.layout.activity_main_layout);

每次新建activity之后,AndroidStudio都会为我们自动生成这一句代码,这句代码究竟干了什么,我们进入Activity类查看。

/**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * @param layoutResID Resource ID to be inflated.
     *
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

getWindow()究竟获取到的是什么,我们继续往下看

public Window getWindow() {
        return mWindow;
    }
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) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this);
		......
}

我们进入PhoneWindow查看它的setContentView方法。

@Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

我们需要把资源加入到mContentParent中,mContentParent是一个ViewGroup对象,当它为空时,执行installDecor();

 private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            
            .......
            
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
		.......

我们看一下generateLayout方法做了些什么,

 protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

这里实例化了DecorView,而DecorView则是PhoneWindow类的一个内部类,继承于FrameLayout。

 protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();

        ......
        
        //是否设置actionBar
        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);
        }
        
        ......
        
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        }
         ......
         
        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ...
        //设置背景
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            mDecor.setWindowBackground(background);

            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);
			//设置title
            if (mTitle != null) {
                setTitle(mTitle);
            }
			//设置title颜色
            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

由以上代码可以看出,先判断是否设置actionBar,然后设置其他属性。之后将设置好的DecorView类的对象contentParent传递给mContentParent,最后通过PhoneWindow中的setContentView方法中的 mLayoutInflater.inflate(layoutResID, mContentParent); 加载activity_main_layout.xml.
DecorView构成
DecorView的作为PhoneWindow的内部类,其作为顶级View被加载到window中,主要包含titlebar和contentParent两个子元素,其中titlebar可以通过系统属性指定其状态。

小结

本节首先介绍了ViewRoot如何t链接WindowManager和DecorView,以及DecorView的构成,为下节介绍View的三大流程做了铺垫。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值