setContentView()做了什么事情?

Posted 哈特谢普苏特

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了setContentView()做了什么事情?相关的知识,希望对你有一定的参考价值。

在onCreate()方法中,我们一般都会遇到setContentView(),那么它到底做了什么?

一个活动继承有两种方式,分别是直接继承Activity,或者继承AppCompatActivity,setContentView()方法的执行就是由这两个基类完成的。

1.Activity

在Activity的setContentView方法主要做了两件事情,首先是通过getWindow获取到window(这个window实际上是phonewindow),调用window的setContentView()方法,其次调用initWindowDecorActionBar()尽心bar的初始化操作。

(1) getWindow

mWindowActivity的成员变量,可以看到在Activity的attach()方法中,mWindow被赋值为PhoneWindow类型。

 那么Activity的attach()方法又是什么时候执行的呢?

在ActivityThread的 performLaunchActivity()方法中,首先通过createBaseContextForActivity(r)获取到基本的context,紧接着通过 activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent)创建当前的activity,最后调用activity的attach方法将 mInstrumentation、activity、context绑定起来。

(2) setContentView

由于getWindow()获取到的实际上是PhoneWindow,因此去看PhoneWindow的setContentView()

 如上图所示,PhoneWindow的setContentView()方法只要做了三件事情。调用installDecor()方法创建decor,调用mLayoutInflater.inflate(),最后将mContentParentExplicitlySet设置为true。

(a)installDecor()

installDocor()方法中,调用了generateDecor(-1)方法和 generateLayout(mDecor)方法,generateDecor()方法实际上就是获取applicationContext并通过applicationContext创建DecorView。

generateLayout()方法中调用了requestFeature()方法

 而requestFeature()方法中首先判断mContentParentExplicitlySet标志位是否为true,如果是的话则抛出异常。

 generateLayout()方法中实际上有很多xml模板,如下图 中使用R.layout.screen_simple

 

 R.layout.screen_simple.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

 R.layout.screen_simple.xml实际上是一个linearLayout布局,内部有viewStub和FrameLayout,其中FrameLayout的id就是content,最终 generateLayout()方法将id为content的FrameLayout赋值给类型为ViewGroup的contentParent并返回

整个结构如下图所示

 

(b) mLayoutInflater.inflate()

inflate()方法多次重载,如果是merge布局,则调用rInflate()方法,否则调用createViewFromTag()方法创建rootView

 在rInflate()方法中也会调用createViewFromTag()方法

 createViewFromTag()方法会判断当前的name中是否还有"."如(constraintLayout就是这种类型,或者自定义的view)调用 onCreateView()方法,否则调用createView()方法。

 

  LayoutInflater 是个抽象类,并没有onCreateView()方法的具体 实现,真正实现该方法的是PhoneLayoutInflater,onCreateView()方法内部其实也调用了createView()。其中for循环中的sClassPrefixList如下图。

 

 createView()方法在LayoutInflater中实现,经过重载后,createView()方法内部通过反射得到当前view的全类名,并通过反射得到的构造器创建实例

 注:好多细节小点:

a.如果使用merge布局,则merge布局需要作为根布局,且参数为true,rInflate()方法中有说明

 

(c) 将mContentParentExplicitlySet设置为true

(3) initWindowDecorActionBar()

2.AppCompatActivity

setContentView()是 AppCompatActivity中的方法

发现是getDelegate().setContentView()

 实际上getDelegate()得到的是AppCompatDelegateImpl,因此去看的setCntentView()方法

 发现首先调用了ensureSubDecor()方法,然后通过mSubDecor找到id为content的控件,清除其中所有的views - removeAllViews()并将当前view添加到容器中。

ensureSubDecor()方法中通过createSubDecor()创建了mSubDecor

createSubDecor()方法很长,首先ensureWindow(),其次调用了mWindow.getDecorView()

 ensureWindow()中调用attachToWindow()方法,而mWindow就是在attachToWindow()中被赋值的。传入的参数mHost实际上是AppCompatActivity,被强转成Activity,并获取activity的window,实际上就是PhoneWindow。

 

      

AppCompatDelegateImpl的构造器中由于此时传入的window为null,因此不会调用attachToWindow()方法

 

 当ensureWindow()方法调用结束后,调用mWindow.getDecorView()。即创建DecorView的过程,同Activity创建DecorView的过程一样。在createSubDecor()中,执行完这两个方法后,开始进行其它操作。

 

 上图这段红框代码的含义是假设使用abc_screen_simple.xml,通过decorView获取到id为action_bar_activity_content的ContentFrameLayout,并使用PhoneWindow寻找id为content的控件并将其赋值给类型为ViewGroup的windowContentView,将windowContentView控件的id设置为NO_ID,然后将decorView获取到id为action_bar_activity_content的ContentFrameLayout的id设置为content。

 

R.layout.abc_screen_simple.xml
<androidx.appcompat.widget.FitWindowsLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/action_bar_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <androidx.appcompat.widget.ViewStubCompat
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/abc_action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include layout="@layout/abc_screen_content_include" />

</androidx.appcompat.widget.FitWindowsLinearLayout>

 

abc_screen_content_include.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <androidx.appcompat.widget.ContentFrameLayout
            android:id="@id/action_bar_activity_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />

</merge>

友情链接:

参考

参考2

以上是关于setContentView()做了什么事情?的主要内容,如果未能解决你的问题,请参考以下文章

setContentView()做了什么事情?

创建一个对象都在内存中做了什么事情

java GC是在什么时候,对什么东西,做了什么事情?

java.lang.String 的 + 号操作到底做了什么事情?

Element-ui 2.8.0版本中提升表格性能,做了哪些事情,原理是什么

关于类方法对象(实例):通过一个例子看一下self都做了哪些事情