Android-Framework: Activity、Window、View三者之间的关系
文章目录
前言
Activity
和View
是大家都比较熟悉一个是Android的四大组件、一个是用于展示各种控件的View
,相对于前两者 Window
在日常开发中比较陌生,今天我们这篇文章就将这三个问题讲清楚。
源代码版本:
- Android framework base 版本:为文章发布时 master分支最新代码 GitHub Android framework base
- AndroidX 版本: 1.0.2 Androidx release
彻底弄明白setContentView()
要想弄清楚 Window
对象是什么使用创建的,就要从最简单的代码入手,一步步得往下查看源码才能解开这个迷惑,那我们就开始吧
首先看以下代码:
class MyTestViewActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_test_view)
//打印View所以父View
LayoutUtils.printAllViewLayout(ll_first_content)
}
}
activity_my_test_view.xml 布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/ll_first_content"
android:orientation="vertical"/>
这里我们有一个 继承与 AppCompatActivity
的 MyTestViewActivity
里面只是调用了 setContentView()
将自定义布局文件传入其中,在 onCreate()
我们调用 printAllViewLayout()
将 自定义布局 LinearLayout
的所有父View 打印出来。
printAllViewLayout()
如下:
fun printAllViewLayout(view: View?) {
if (view == null) {
return
}
//打印view所有的parent
var parent = view.parent
while (parent != null) {
Log.d("printAllViewLayout: ","parent : $parent")
parent = parent.parent
}
}
上述代码运行之后打印如下信息:
printAllViewLayout:: parent : androidx.appcompat.widget.ContentFrameLayout
printAllViewLayout:: parent : androidx.appcompat.widget.FitWindowsLinearLayout
printAllViewLayout:: parent : android.widget.FrameLayout
printAllViewLayout:: parent : android.widget.LinearLayout
printAllViewLayout:: parent : com.android.internal.policy.PhoneWindow$DecorView
printAllViewLayout:: parent : android.view.ViewRootImpl
这里我们看到打印出很多父View
类型、 ContentFrameLayout
、FitWindowsLinearLayout
、 FrameLayout
、 LinearLayout
以及 DecorView
和 ViewRootImpl
这些都是什么时候添加的呢?又起到什么作用呢?我们带着这两个问题从源码的角度进行一旦究竟吧
首先我们需要从 setContentView()
的具体实现开始分析
AppCompat 层实现
//AppCompatActivity 实现
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
//AppCompatDelegate 实现类 AppCompatDelegateImpl
@Override
public void setContentView(int resId) {
//创建DecorView
ensureSubDecor();
//加载自定义布局 并添加到 contentParent 中
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
AppCompatActivity
中 setContentView()
是有其代理类实现,AppCompatDelegateImpl
类中 setContentView()
只做了两件事
- 执行 ensureSubDecor() 创建自定义ViewGroup对象 :mSubDecor
- 加载自定义布局并添加到contentParent中 (此时的contentParent 是
AppCompatDelegateImpl
类中自定义的ViewGroup)
ensureSubDecor()
我们继续往下看 ensureSubDecor()
具体是怎么实现的
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
//省略不重要代码
}
}
ensureSubDecor()
中很显然只有一句比较重要的代码 调用了 createSubDecor()
而 mSubDecorInstalled 只是一个标志位用于标识是否已经初始化过了,当执行完 createSubDecor()
会置成 true 因此保证只会初始化一次。
我们接着往下看 createSubDecor()
具体做了什么
private ViewGroup createSubDecor() {
//省略不重要代码...
// 确保window已经加载了 DecorView 如果没有则进行创建并添加到window上
mWindow.getDecorView();
//根据不同的 theme 加载不同的layout 布局文件
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//存在 windowTitle 情况下加载 带有actionBar 的布局文件
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) {
// 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);
}
} else {
if (mOverlayActionMode) {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
//没有windowTitle 默认情况下加载此布局
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
}
// 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中
mWindow.setContentView(subDecor);
// 返回自定义ViewGroup
return subDecor;
}
上述createSubDecor()
只做了三件大事,具体是:
- 调用
Window
中getDecorView()
创建DecorView、创建 ContentParent ViewGroup 并加入到DecorView中 - AppCompat 层创建自定义样式 ViewGroup并设置相关适配、监听等
- 调用
Window
中setContentView()
将创建的ViewGroup 添加到 ContentParent 中
接下来我们先一件一件拆开来分析,首先我们来看第一件事 也就是 getDecorView()
, Window
是一个抽象类,而 Window
的唯一实现类 是 com.android.internal.policy.PhoneWindow
所以我们就看一下 PhoneWindow
是怎么实现的。
PhoneWindow 的 getDecorView()实现
PhoneWindow
的 getDecorView()
很显然这个方法是个单例的,只会创建一个 DecorView
对象,而且具体实现是在 installDecor()
中
@Override
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
//安装 DecorView 布局
private void installDecor() {
if (mDecor == null) {
//创建 DecorView
mDecor = generateDecor(-1);
}
if (mContentParent == null) {
//创建 ContentParent 布局
mContentParent = generateLayout(mDecor);
}
//省略无关代码
}
installDecor()
方法比较长,但实际只干了两件事:
- 如果mDecor 为空则调用
generateDecor()
创建 DecorView 对象 - 如果 mContentParent 为空则调用
generateLayout()
创建ContentParent 对象
generateDecor() 创建DecorView
generateDecor()
直接创建了一个新的 DecorView