AndroidDecorView添加机制

概述

一直以来对AndroidView的绘制流程没有系统的了解,今天基于Android8.1简单的介绍下DecorView是怎么显示出来的

源码基于Android8.1

Activity的setContentView说起

给activity设置布局的时候都是在setcontentview开始的,setcontentview有几个重载的方法
我们打开activity.java源码可以看到

   public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
   public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

同名的方法

   public void setContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().setContentView(view, params);
        initWindowDecorActionBar();
    }

这三个方法,应用层可以根据需要传入不同的参数来定制自己的页面
可以看到上面的三个方法都有一个共同点就是都有调用getWindow(),找到getwindow()方法我们可以看下他的实现

public Window getWindow() {
return mWindow;
}

返回的是一个mWindow对象,我们找到他的定义发现其数据类型是private Window mWindow,这里我们仅仅知道他的数据类型,下面我们跟踪到其赋值初始化的地方,作为activity的一个私有成员,他的初始化肯定是在activity.java中完成
我们跟踪到其在attch方法中有赋值的时机

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, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
    mFragments.attachHost(null /*parent*/);
    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);
    }
mUiThread = Thread.currentThread();
    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    if (voiceInteractor !=null) {
if (lastNonConfigurationInstances !=null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        }else {
mVoiceInteractor =new VoiceInteractor(voiceInteractor, this, this,
                   Looper.myLooper());
        }
}
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);
}

可以看到其数据类型是PhoneWindow
我们找到PhoneWindow.java
public class PhoneWindow extends Window可以看出PhoneWindow是继承window的
继续跟进到Window.java,在文件的注释中我们看到

*
The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
目前为止PhoneWindow是window的唯一子类

接下来我们就要跟踪下PhoneWindow中setcontentview的具体实现了,跟Activity一一对应的也有三个重载方法

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();
    }
mContentParentExplicitlySet =true;
}
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// 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)) {
view.setLayoutParams(params);
        final Scene newScene =new Scene(mContentParent, view);
        transitionTo(newScene);
    }else {
mContentParent.addView(view, params);
    }
mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb !=null && !isDestroyed()) {
cb.onContentChanged();
    }
mContentParentExplicitlySet =true;
}

对public void setContentView(int layoutResID)进行分析

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();
}
mContentParentExplicitlySet =true;

对setcontentView(int layoutResID)进行分析

如果mContentParent为null的话那么执行installDecor();操作
首先我们看下mContentparent此时到底为不为null.我们找到ViewGroup mContentParent;的定义
他的数据类型是ViewGroup,在Activity中全文搜索mContentparent有且仅有installDecor中赋值
那么我们可以肯定得是此时mContentParent肯定是null,执行installDecor,找到installDecor方法

private void installDecor() {
mForceDecorInstall =false;
    if (mDecor ==null) {
     mDecor = generateDecor(-1);
     mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
     mDecor.setIsRootNamespace(true);
     if (!mInvalidatePanelMenuPosted &&mInvalidatePanelMenuFeatures !=0) {
     mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
}else {
mDecor.setWindow(this);
    }
if (mContentParent ==null) {
mContentParent = generateLayout(mDecor);

在此方法中mDecor = generateDecor(-1);用于初始化mDecor
mContentParent = generateLayout(mDecor);用于初始化mContentParent,初始化完成之后根据应用设置的一些主题,feature等来完成一些初始化操作
我们重点关注下generateDecor,generateLayout这两个方法

return new DecorView(context, featureId, this, getAttributes());根据feature和窗口属性生成的是一个DecorView,我们找到DecorView的构造函数
DecorView(Context context, int featureId, PhoneWindow window,
        WindowManager.LayoutParams params) {

可以看出DecorView是跟PhoneWindow相关联的,至少可以肯定的是一个PhoneWindow对应一个DecorView
从DecorView的源码中我们可以看出DecorView extends FrameLayout其是FrameLayout的子类
初始化过程中有加载显示和隐藏动画等相关的操作,而framelayout是viewgroup的直接子类从而也是view的间接子类
上面我们可以得出的结论是DecorView也是一个视图
下面我们分析下
generateLayout(mDecor);这个方法比较长,但是有一个关键点

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent ==null) {
throw new RuntimeException("Window couldn't find content container view");
}

有这样一段代码,在此处生成了一个contentParent,他的id是一个常量
com.android.internal.R.id.content,这行代码之前基本上是解析style和一些flag
上面我们就是contentparent和mDecor的初始化
下面有一句比较关键的话
mLayoutInflater.inflate(layoutResID, mContentParent);
我们进入到LayoutInflater看看layoutResID是怎么利用的

try {
return inflate(parser, root, attachToRoot);
}finally {
parser.close();
}

再进入到inflate方法
我们可以看到

// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root !=null && attachToRoot) {
root.addView(temp, params);
}这个root就是我们传进去的mContentParent

也就是说应用层设置的布局文件是被添加到mcontentparent上面的
上面的分析我们可以得出的结论是有这样的层次关系
DecorView-->mContentParent---->activity中的布局
下面解析下activity的布局是怎样加入到mContentParent中去的

// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);

可以看到有布局和重绘的操作
DecorView何时显示出来
这个需要研究activity的启动流程
一个activity就是对应一个ActivityRecord对象,里面保存着一些相关的信息
我们知道一个应用默认情况就是一个进程,那么随之也就是有主线程存在,也就是所谓的main方法
首先找到ActivityThread的handleLaunchActivity方法,至于为啥是这个方法,涉及到activity的启动流程
在其他文章中再介绍,在这里会实例化一个activity对象
实例化完成之后会执行handleResumeActivity方法

if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}

最终的实现在activity中,此时会将mDecorView设置成可见

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded =true;
    }
mDecor.setVisibility(View.VISIBLE);
}

在上面的方法中可以看到
wm会将mDecor添加到一个wm对象上,本质上是一个WindowManager
Windowmanager的实现类是WindowManagerImpl

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

最终是通过mGolbal添加的
关键的代码是

// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
}catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
    if (index >=0) {
removeViewLocked(index, true);
    }
throw e;
}
// 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.
requestLayout();

关键的代码是requestLayout

@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();
    }
}

将mTraversalRunnablepost到消息队列中去

final class TraversalRunnableimplements 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这个方法里面进行了一系列的操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();等一系列的操作
这样DecorView也被添加到了一个窗口上面

你可能感兴趣的:(AndroidDecorView添加机制)