Activity setContentView的过程

setContentView的过程

基于sdk30

  • setContentView是展示页面重要的方法,需要通过setContentView才能xml显示到页面上来
  • setContentView有2种传参方式一种是通过layoutID一种是直接传递view 本质上都是一样
  • 值得注意的是继承AppCompatActivity和Activity的流程是稍有差别的。

Activity的过程

  • 源码太多只抄了关键代码
  • getWindow().setContentView(layoutResID);
  • getWindow()指的是Window,Window是一个抽象类,唯一实现类是PhoneWindow,MockWindow不算他是在com.android.setupwizardlib.test.util目录
  • Activtiy的window是在attach方法中实例化的,attach是在ActivityThread.performLaunchActivity放中调用,(多嘴一句Activtiy,Application的实例化也是在这块实现的,Activtiy和Application都是通过反射newInstance去创建的)


    1
2
  • 通过上面分析,其实最终调用的是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();
        }
        mContentParentExplicitlySet = true;
    }
  • 开始分析installDecor方法,直接看generateDecor怎么实现DecorView(mDecor是DecorView顶层试图最外面的view)
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);
        }
        //...
}
  • generateDecor方法,直接将context和WindowManager.LayoutParams实例化DecorView(注意的是如果是mUseDecorContext为true,也就是通过PhoneWindow(Context context, Window preservedWindow,
    ActivityConfigCallback activityConfigCallback)这构造参数实现,才会使mUseDecorContext为tur。而这只有Activity new出来的PhoneWindow才调用了这个构造, 使用这里DecorView的context才会是getApplicationContext())


    3
protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, this);
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
  • 创建完DecorView的过程看完继续分析mContentParent = generateLayout(mDecor); 这部分其实挺操蛋的只要关注2个点就行了
    protected ViewGroup generateLayout(DecorView decor) {
// ...省略
     mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
      ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
      //...省略
    }
  • 上面就是通过判断不同属性去setFlags以及拿到确定layoutResource的系统预设的xml文件


    4
  • onResourcesLoaded这个就是将layoutResource添加到DecorView里面去

5
  • findViewById(ID_ANDROID_CONTENT)这个就是先通过DecorView的findViewById然后在通过Viewgroup的findViewTraversal循环拿到view。(Activtiy和Diaolog的findViewById都是通过DecorView的findViewById去找的)
  • 看setContentView的最后一部分
    //判断是否有过度动画
 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
  • hasFeature(FEATURE_CONTENT_TRANSITIONS)有没有过度动画,如果有就去调用transitionTo
 private void transitionTo(Scene scene) {
        if (mContentScene == null) {
            scene.enter();
        } else {
            mTransitionManager.transitionTo(scene);
        }
        mContentScene = scene;
    }
  • 第一次mContentScene肯定是null的直接看 scene.enter();
public void enter() {

        // Apply layout change, if any
        if (mLayoutId > 0 || mLayout != null) {
            // empty out parent container before adding to it
            getSceneRoot().removeAllViews();

            if (mLayoutId > 0) {
                LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
            } else {
                mSceneRoot.addView(mLayout);
            }
        }

        // Notify next scene that it is entering. Subclasses may override to configure scene.
        if (mEnterAction != null) {
            mEnterAction.run();
        }

        setCurrentScene(mSceneRoot, this);
    }

  • 看到这得出一个结论, 无论有没有动画都有会走LayoutInflater.inflater方法
  • 最后看下inflater到底干啥了,通过套娃调用找到inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)传这3个参数的inflate
  • 知道XmlResourceParser这个是个解析xml的东西就好了,感兴趣的可以百度下。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

           //...省略

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                  

                    //...省略
                    rInflateChildren(parser, temp, attrs, true);
        
                  if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                    //将view添加到rootview
                        root.addView(temp, params);
                    }
                    }
            return result;
        }
    }
  • 先看if (TAG_MERGE.equals(name)) 这个判断条件是意思是如果根布局是个merge那就走rInflate这个方法(关于merge、include、ViewStub感兴趣的可以百度下)
 void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final String name = parser.getName();

            if (TAG_REQUEST_FOCUS.equals(name)) {
                pendingRequestFocus = true;
                consumeChildElements(parser);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }

        if (pendingRequestFocus) {
            parent.restoreDefaultFocus();
        }

        if (finishInflate) {
            parent.onFinishInflate();
        }
    }
  • 通过while遍历去判断xml的name去解析不同的标签直接关注else的重点createViewFromTag
  View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        //...省略

        try {
            View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        }
        //...省略
    }
  • 先看下tryCreateView这个
public final View tryCreateView(@Nullable View parent, @NonNull String name,
        @NonNull Context context,
        @NonNull AttributeSet attrs) {
        if (name.equals(TAG_1995)) {
            // Let's party like it's 1995!
            return new BlinkLayout(context, attrs);
        }

        View view;
        if (mFactory2 != null) {
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }

        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }

        return view;
    }
  • 发现这里是通过mFactory2或者mFactory去创建view的,如果上面createViewFromTag的 View view = tryCreateView(parent, name, context, attrs);不为空了就没法走LayoutInflater自己的onCreateView了,mFactory和mFactory2是在构造方法的时候赋值的
protected LayoutInflater(LayoutInflater original, Context newContext) {
        mContext = newContext;
        mFactory = original.mFactory;
        mFactory2 = original.mFactory2;
        mPrivateFactory = original.mPrivateFactory;
        setFilter(original.mFilter);
        initPrecompiledViews();
    }
  • 去探究下LayoutInflater的创建过程先回到PhoneWindow
 public PhoneWindow(Context context) {
       super(context);
       mLayoutInflater = LayoutInflater.from(context);
       mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
               DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
   }
  • 结合上面的内容LayoutInflater其实在attach通过实例化Phoneview的时候就创建了直接看LayoutInflater.from
   public static LayoutInflater from(Context context) {
       LayoutInflater LayoutInflater =
               (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       if (LayoutInflater == null) {
           throw new AssertionError("LayoutInflater not found.");
       }
       return LayoutInflater;
   }

  • context.getSystemService(),这下子得回到Activity去看了,毕竟这里的context是Activity继承的ContextThemeWrapper的(ContextThemeWrapper继承ContextWrapper,ContextWrapper继承Context抽象类)
  • getSystemService通过super之后到了ContextThemeWrapper的getSystemService
 @Override
   public Object getSystemService(String name) {
       if (LAYOUT_INFLATER_SERVICE.equals(name)) {
           if (mInflater == null) {
               mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
           }
           return mInflater;
       }
       return getBaseContext().getSystemService(name);
   }
  • 看到如果是LAYOUT_INFLATER_SERVICE并且mInflater为null直接走cloneInContext,看到这又有点小蒙圈了怎么又用到了LayoutInflater.from()这不是是玩蛇么。直接去看下getBaseContext()是何方神圣
  • 回到Activtiy的attach方法看到传递了一个Context然后调用了attachBaseContext(context)才赋值了getBaseContext()。莫非...
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, IBinder assistToken) {
        attachBaseContext(context);
        //省略...
        }

  • ActivityThread.performLaunchActivity调用activity.attach的地方
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       //省略...
        ContextImpl appContext = createBaseContextForActivity(r);
         //省略...
          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,
                        r.assistToken);
                        //...
  • 所以到最后终归是走了ContextImpl的getSystemService
@Override
    public Object getSystemService(String name) {
        //...省略
        return SystemServiceRegistry.getSystemService(this, name);
    }
  • SystemServiceRegistry的getSystemService
 public static Object getSystemService(ContextImpl ctx, String name) {
        if (name == null) {
            return null;
        }
        final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        //...省略
         final Object ret = fetcher.getService(ctx);
       //...省略
        return ret;
 }
  • SYSTEM_SERVICE_FETCHERS这个在SystemServiceRegistry类被加载的时候就去put了大量对象,后续只要通过名称就可以拿到对象了比如LAYOUT_INFLATER_SERVICE
   registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
  • 所以上面LayoutInflater.from(getBaseContext()).cloneInContext(this)实际就是调用了PhoneLayoutInflater.cloneInContext
   public LayoutInflater cloneInContext(Context newContext) {
        return new PhoneLayoutInflater(this, newContext);
    }
    
     protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
        super(original, newContext);
    }
  • 这样看来现在LayoutInflater的mFactory和mFactory2还是空的,上面的View view = tryCreateView(parent, name, context, attrs);为null这样就继续走onCreateView或者createView了
 View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
  • 看下if判断这个-1==name.indexOf('.'),如果name不包含.比如TextView,ImgaeView走onCreateView,否则如自定义com.xxx.xxxView,androidx.constraintlayout.widget.ConstraintLayout, androidx.recyclerview.widget.RecyclerView这些需要在xml使用全路径的view就走createView
  • 先看onCreateView,由于上面LayoutInflater的创建是mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this),所以要去看下PhoneLayoutInflater.onCreateView也没有重写。
private static final String[] sClassPrefixList = {
        "android.widget.",
        "android.webkit.",
        "android.app."
    };
  protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        for (String prefix : sClassPrefixList) {
            try {
                View view = createView(name, prefix, attrs);
                if (view != null) {
                    return view;
                }
            } catch (ClassNotFoundException e) {
                // In this case we want to let the base class take a crack
                // at it.
            }
        }

        return super.onCreateView(name, attrs);
    }
    
  • 先假设generateLayout中 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);,mLayoutInflater的layout为R.layout.screen_simple,他的第一个布局是LinearLayout而LinearLayout的全路径为android.widget.LinearLayout。接着往createView看,PhoneLayoutInflater没有重写createView,所以又回到了LayoutInflater.createView
public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        
        Constructor<? extends View> constructor = sConstructorMap.get(name);
      
        Class<? extends View> clazz = null;
            //...
        try {
            //...

            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);
                        //...
                constructor = clazz.getConstructor(mConstructorSignature);
                //...省略
           】

           //...
                final View view = constructor.newInstance(args);
               //..
                return view;
            } 
            //...省略
    }
  • 看到Class是通过prefix加名称补全得到的view,再通过constructor.newInstance去实例化,如LinearLayout通过循环补全android.widget.LinearLayout才能用反射创建出来这也就是说名为啥TextView,ImageView,LinearLayout这些不用在xml写全路径(这些都在 "android.widget.",
    "android.webkit.",
    "android.app."目录里面这里通过for循环sClassPrefixList的方式自动补全了路径),其他的需要。
  • 到了这里if (-1 == name.indexOf('.'))的else判断也不用再去看了,最终都是通过全路径得到class,通过反射实例化出来。
  • 看下rInflateChildren这个创建子view
 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
            boolean finishInflate) throws XmlPullParserException, IOException {
        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
    }
  • rInflate已经扯完了
  • 回到inflate中if (TAG_MERGE.equals(name))这个判断的地方看下else部分
   if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                    //...
                    rInflateChildren(parser, temp, attrs, true);
                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }
                     // 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);
                    }
                }
  • 又是createViewFromTag和rInflateChildren,到这流程其实已经扯完了。inflate其实就是通过xml解析出来的名称
    、AttributeSet去反射创建view然后addView到rootview。
  • 回到PhoneWindow中setContentView那句代码
 mLayoutInflater.inflate(layoutResID, mContentParent);
  • 综合上面的分析,不就是将自己Activty的xml布局添加到父布局么

成热打铁下看下AppCompatActivity的setContentView过程

 @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
    
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
    
    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else {
            return new AppCompatDelegateImplV14(context, window, callback);
        }
    }
    AppCompatDelegateImplN继承AppCompatDelegateImplV23
    AppCompatDelegateImplV23继承AppCompatDelegateImplV14
    AppCompatDelegateImplV14继承AppCompatDelegateImplV9
    AppCompatDelegateImplV9继承AppCompatDelegateImplBase实现MenuBuilder.Callback、 LayoutInflater.Factory2接口
  • 通过一番源码跳转到AppCompatDelegateImplV9的setContentView
   @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
    
    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            //重点
            mSubDecor = createSubDecor();

            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                onTitleChanged(title);
            }

            applyFixedSizeWindow();

            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true;

            // Invalidate if the panel menu hasn't been created before this.
            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
            // being called in the middle of onCreate or similar.
            // A pending invalidation will typically be resolved before the posted message
            // would run normally in order to satisfy instance state restoration.
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!isDestroyed() && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }
    
    private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }

        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
        }
        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
            requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
        }
        mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
        a.recycle();

        //重点
        // Now let's make sure that the Window has installed its decor by retrieving it
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;


        if (!mWindowNoTitle) {
            if (mIsFloating) {
                // If we're floating, inflate the dialog title decor
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);

                // Floating windows can never have an action bar, reset the flags
                mHasActionBar = mOverlayActionBar = false;
            } else if (mHasActionBar) {
                /**
                 * This needs some explanation. As we can not use the android:theme attribute
                 * pre-L, we emulate it by manually creating a LayoutInflater using a
                 * ContextThemeWrapper pointing to actionBarTheme.
                 */
                TypedValue outValue = new TypedValue();
                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

                Context themedContext;
                if (outValue.resourceId != 0) {
                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
                } else {
                    themedContext = mContext;
                }

                // Now inflate the view using the themed context and set it as the content view
                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                        .inflate(R.layout.abc_screen_toolbar, null);

                mDecorContentParent = (DecorContentParent) subDecor
                        .findViewById(R.id.decor_content_parent);
                mDecorContentParent.setWindowCallback(getWindowCallback());

                /**
                 * Propagate features to DecorContentParent
                 */
                if (mOverlayActionBar) {
                    mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
                }
                if (mFeatureProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
                }
                if (mFeatureIndeterminateProgress) {
                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                }
            }
        } else {
            if (mOverlayActionMode) {
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }

            if (Build.VERSION.SDK_INT >= 21) {
                // If we're running on L or above, we can rely on ViewCompat's
                // setOnApplyWindowInsetsListener
                ViewCompat.setOnApplyWindowInsetsListener(subDecor,
                        new OnApplyWindowInsetsListener() {
                            @Override
                            public WindowInsetsCompat onApplyWindowInsets(View v,
                                    WindowInsetsCompat insets) {
                                final int top = insets.getSystemWindowInsetTop();
                                final int newTop = updateStatusGuard(top);

                                if (top != newTop) {
                                    insets = insets.replaceSystemWindowInsets(
                                            insets.getSystemWindowInsetLeft(),
                                            newTop,
                                            insets.getSystemWindowInsetRight(),
                                            insets.getSystemWindowInsetBottom());
                                }

                                // Now apply the insets on our view
                                return ViewCompat.onApplyWindowInsets(v, insets);
                            }
                        });
            } else {
                // Else, we need to use our own FitWindowsViewGroup handling
                ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
                        new FitWindowsViewGroup.OnFitSystemWindowsListener() {
                            @Override
                            public void onFitSystemWindows(Rect insets) {
                                insets.top = updateStatusGuard(insets.top);
                            }
                        });
            }
        }

        if (subDecor == null) {
            throw new IllegalArgumentException(
                    "AppCompat does not support the current theme features: { "
                            + "windowActionBar: " + mHasActionBar
                            + ", windowActionBarOverlay: "+ mOverlayActionBar
                            + ", android:windowIsFloating: " + mIsFloating
                            + ", windowActionModeOverlay: " + mOverlayActionMode
                            + ", windowNoTitle: " + mWindowNoTitle
                            + " }");
        }

        if (mDecorContentParent == null) {
            mTitleView = (TextView) subDecor.findViewById(R.id.title);
        }

        // Make the decor optionally fit system windows, like the window's decor
        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
        //重点
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
        //重点
        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            //重点
            windowContentView.setId(View.NO_ID);
            //重点
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        //重点
        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);

        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
            @Override
            public void onAttachedFromWindow() {}

            @Override
            public void onDetachedFromWindow() {
                dismissPopups();
            }
        });

        return subDecor;
    }
    
  • 这里AppCompatActivity通过AppCompatDelegateImplV9包了一层layou去调用了PhoneWindow的setContentView的方法剩下的又是Activity的流程了
  • 注意的是下面几行行代码
  1. 找到ContentFrameLayout
  final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);
  1. 找到android.R.id.content 这个其实就是activity流程中PhoneWindow.generateLayout方法创建的layout布局id如下图
    final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
6
  1. 把原来Activity中设定的布局名称设置成null
   windowContentView.setId(View.NO_ID);
  1. 把现在设定的布局名称设置成content,父布局就变成AppCompatActivity创建的ContentFrameLayout了,就这样完成了父布局的替换
      contentView.setId(android.R.id.content);
  • setContentView方法中,不也是将自己Activty的xml布局添加到父布局么,和Activity不同的是这里通过包装操作将原来的父布局变成了AppCompatActivity的contentParent了
LayoutInflater.from(mContext).inflate(resId, contentParent);

到这里流程讲完了总结一下

Activity被ActivityThread创建出来调用了attch方法拿到ContextImpl,调用PhoneWindow的setContentView方先创建了DecorView,通过一番判断设定了flag和父布局xml,然后把这个父布局添加到DecorView,在把自己Activty的xml添加到父布局。

AppCompatActivity在Activity的基础上添加了层父布局,通过改名替换的操作替换了Activity的父怒拒,后续自己activity的xml将添加这父布局

LayoutInflater的inflate是将xml解析出来,通过全路径(ImageView、TextView这些通过循环补全)名称和属性去反射创建View,并将这个view添加到参数root布局里面

流程图
  • Activtiy
Activtiy
  • AppCompatActivity


    AppCompatActivity
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容