天天看点

View的绘制流程一(视图的加载)

文章目录

    • 视图层级关系
      • PhoneWindow的初始化
        • ActivityThread.performLaunchActivity
      • Activity的setContentView
        • PhoneWindow.setContentView
        • PhoneWindow.installDecor
        • PhoneWindow.generateDecor
        • PhoneWindow.generateLayout
      • 总结

视图层级关系

View的绘制流程一(视图的加载)

PhoneWindow的初始化

PhoneWindow 伴随着 Activity 的创建而创建, 而 ActivityThread 掌握着 Activity 的创建.

在ActivityThread的performLaunchActivity 方法中,完成了PhoneWindow的初始化和activity的创建,初始化完 Activity 后, 就调用 Activity 的 onCreate 方法了.

ActivityThread.performLaunchActivity

细节如下:

  1. 在performLaunchActivity中创建了Activity对象以及Context, theme, PackageInfo 等,且初始化了一个Windows
  2. 之后调用了方法activity.attach实例化了 PhoneWindow
  3. 调用 Activity 的 onCreate 方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
// 创建 Activity的appContext
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
//  创建  Activity
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ...
    } catch (Exception e) {
        ...
    }
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        ...
        if (activity != null) {
            ...
            Window window = null;
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
// window 创建了
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);

// 初始化 Activity 相关的内容, Activity 的attach这个方法中关联了 window
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);
            ...
            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;
}

           

Activity的setContentView

在上述执行完成之后,Activity的onCreat()被执行,进而执行Activity的setContentView,其会调用PhoneWindow.setContentView方法

PhoneWindow.setContentView

public void setContentView(int layoutResID) {
    if (mContentParent == null) {
//installDecor 这个方法中, 初始化了 DecorView, mContentParent, 初始化了比如标题,icon, logo, 是否全屏等该 window 的一些基础属性
// 初始化 DecorView
        installDecor();   
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//如果没有过渡动画, 并且已经创建过 DecorView
        mContentParent.removeAllViews();
    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 场景转换(过渡动画)
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
        transitionTo(newScene);
    } else {
// 开始填充我们设置的 XML 布局, 容器为 mContentParent
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}
           

阅读上面代码可知, mContentParent 就是装载我们所有内容的根容器(ViewGroup)了. 在这里, 最关键的代码就是 mLayoutInflater.inflate(layoutResID, mContentParent) , 它就是填充我们的布局的代码了.

PhoneWindow.installDecor

  • 初始化mContentParent和顶级ViewGroup :mDecorView
private void installDecor() {
        ...
    mForceDecorInstall = false;
    if (mDecor == null) {
// generateDecor方法生成了 DecorView
        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) {
// generateLayout方法生成装载我们的布局的容器
        mContentParent = generateLayout(mDecor);
        ...
}

           

PhoneWindow.generateDecor

  • 初始化mDecorView
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();
    }
// 实例化了一个 DecorView
    return new DecorView(context, featureId, this, getAttributes());
}
           

PhoneWindow.generateLayout

  • 初始化mContentParent以及window的title, titleColor,ActionBar等
protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
// 获取当前 window 的 主题
    TypedArray a = getWindowStyle();
... // 初始化样式, 例如 windowNoTitle, windowActionBar, windowIsTranslucent, windowSoftInputMode 等等
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    ... 
   //设置 windowBackground, title, titleColor 等
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    mDecor.finishChanging();
    return contentParent;
}
           

总结

  1. 在 ActivityThread 的 performLaunchActivity 中, 创建了 PhoneWindow 并调用了 Activity 的 attach 方法
  2. 在 Activity 的 attach 方法中, 关联了 PhoneWindow
  3. 在 Activity 的 setContentView 方法中 获取了 PhoneWindow , 并将 layoutResID 传入 PhoneWindow 的 setContentView 中
  4. 在 PhoneWindow 的 setContentView 中, 初始化了 DecorView,然后初始化mContentParent,把加载出来的这个布局文件对应的View赋值给mDecor
  5. 在完成上述逻辑后,会回到installDecor中,再调用LayoutInflater的inflate方法解析我们自己的XML布局文件,也就是我们在自己的Activity中onCreate方法中调用setContentView方法中传入的Layout ID,把解析出来的View添加mContentParent中,这样就把我们自己的Layout添加到mDecor中。
View的绘制流程一(视图的加载)
  1. 在onCreate方法中也即Activity生命周期的第一个方法我们通常会调用setContentView(layoutID),这个过程我们在前面已经说过了,紧接着进行生命周期的第二个方法onResume(通过AMS及Handler机制),最终会通过Handler消息分发机制最终调用到ActivityThread中mH中handleMessage()方法调用what=RESUME_ACTIVITY的逻辑判断体内,即调用handleResumeActivity方法。
  2. 在handleResumeActivity方法中会调用WindowManagerImpl的addView(mDecor,LayoutParam)把前面创建的DecorView(即从系统加载的那个XML布局文件,同样也包含了我们自己的布局文件)。
  3. 在WindowManagerImpl内部其实是通过WindowManagerGlobal去完成addView的操作的。
  4. 在WindowManagerGlobal的addView方法中创建了实例对象ViewRootImpl的,调用了其setView方法,在后面就调用到了performTraversals
  5. 在后面就依次调用了performMeasure,performLayout,performDraw

    注:详细看此处

参考1

参考2