Activity启动流程源码分析-setContentView源码阅读

问题描述

做Android开发的朋友们都熟悉这个方法: setContentView(R.layout.activity_main);

喜欢思考的朋友们肯定想知道为什么一上来就要执行这个方法呢???

精华所在

setContentView 只是创建DecorView 把我们的布局加载到了DecorView

问题剖析

我们按住 ctr 并用鼠标右键 setContentView(R.layout.activity_main) 会来到如下源码:

有些人的 MainActivity 可能继承 AppCompatActivity,问题不大,我给大家讲继承 Activity 的,道理是相通的,大家可以自己尝试理解

/**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *
     * 从布局资源中设置活动内容。 该资源将被inflated,将所有顶层视图添加到活动中。
     *
     * @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) {
        // 获取Window 调用window的setContentView方法,发现是抽象类,所以需要找具体的实现类 
        // PhoneWindow
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.

 *顶层窗口外观和行为策略的抽象基类。 该类的实例应作为添加到窗口管理器的顶层视图。它提供了标准的UI 
 *策略,如背景、标题区域、默认键处理等。
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 *
 *这个抽象类唯一存在的实现是android.view.PhoneWindow,当需要一个Window时,你应该实例化它。
 */
public abstract class Window{


}

当你找到 PhoneWindow 的时候,你发现它并不在 android.view 包里,新版的在 android.internal.policy 下

@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) {
        // 如果mContentParent 等于空,调用installDecor();
            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 {
         // 把我们自己的布局layoutId加入到mContentParent,我们set进来的布局原来是放在这里面的
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

这个时候,我们找到了 setContentView 的根源 ,在里面有 一个方法值的我们关注 installDecor() 于是,我们点进去

    // This is the top-level view of the window, containing the window decor.
    // 这句话是不是很熟悉
    private DecorView mDecor;

    private void installDecor() {    
        if (mDecor == null) {
           // 先去创建一个  DecorView 
           mDecor = generateDecor(-1);
        }
        // ......
        // 省略调一些代码,看着晕,不过这也太省了。
        if (mContentParent == null) {
           mContentParent = generateLayout(mDecor);
        }
    }
    
    // generateDecor 方法
    protected DecorView generateDecor(int featureId) {
        // 就是new一个DecorView ,DecorView extends FrameLayout 不同版本的源码有稍微的区别,
        // 低版本DecorView 是PhoneWindow的内部类,高版本是一个单独的类,不过这不影响。
        return new DecorView(context, featureId, this, getAttributes());
    }

    protected ViewGroup generateLayout(DecorView decor) {
        // Inflate the window decor.
        // 我看你到底怎么啦
        int layoutResource;
        // 都是一些判断,发现 layoutResource = 系统的一个资源文件,
        if(){}else if(){}else if(){
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }
        
        mDecor.startChanging();
        // 把布局解析加载到  DecorView 而加载的布局是一个系统提供的布局,不同版本不一样
        // 某些源码是 addView() 其实是一样的
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        //  ID_ANDROID_CONTENT 是 android.R.id.content,这个View是从DecorView里面去找的,
        //  也就是    从系统的layoutResource里面找一个id是android.R.id.content的一个FrameLayout
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        
        // 返回
        return contentParent;
    }

上面说了那么多,估计大家思路都有点乱了,下面,我给大家整理一下:

  • Activity里面设置setContentView(),其实是调用PhoneWindow中的setContentView(),在PhoneWindow中通过 installDecor() 实例化一个DecorView。(提醒大家,installDecor() 和 setContentView() 都在PhoneWindow 类中,所以有很多 变量 是公用的)
  • mDecor = generateDecor(-1); 这句话是 实例化一个DecorView 的关键,
  • mContentParent = generateLayout(mDecor); 将 mDecor (实例化一个DecorView)传进去进行一系列的系统加工(这里包括是否有头部等系统加工,便很好解决了为什么要在 setContentView() 之前执行设置Window的Flag或者requestWindowFeature()才会起作用)
  • mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);这个就是给 mDecor 添加配置的关键语句,最后通过 return contentParent 返回给 mContentParent(这个是全局变量)

猜你喜欢

转载自blog.csdn.net/qq_41885673/article/details/115254119