Android-Framework: Activity、Window、View三者之间的关系

本文详细探讨了Android中Activity、Window和View三者的关系,通过源码分析了setContentView()的执行过程,包括AppCompat层的实现、PhoneWindow的getDecorView()以及View是如何显示在Activity上的。通过一系列步骤揭示了DecorView、ContentParent的创建以及ViewRootImpl在显示过程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android-Framework: Activity、Window、View三者之间的关系

前言

ActivityView是大家都比较熟悉一个是Android的四大组件、一个是用于展示各种控件的View,相对于前两者 Window 在日常开发中比较陌生,今天我们这篇文章就将这三个问题讲清楚。

源代码版本:

彻底弄明白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"/>

这里我们有一个 继承与 AppCompatActivityMyTestViewActivity 里面只是调用了 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 类型、 ContentFrameLayoutFitWindowsLinearLayoutFrameLayoutLinearLayout 以及 DecorViewViewRootImpl 这些都是什么时候添加的呢?又起到什么作用呢?我们带着这两个问题从源码的角度进行一旦究竟吧

首先我们需要从 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();
}

AppCompatActivitysetContentView() 是有其代理类实现,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() 只做了三件大事,具体是:

  1. 调用WindowgetDecorView()创建DecorView、创建 ContentParent ViewGroup 并加入到DecorView中
  2. AppCompat 层创建自定义样式 ViewGroup并设置相关适配、监听等
  3. 调用WindowsetContentView() 将创建的ViewGroup 添加到 ContentParent 中

接下来我们先一件一件拆开来分析,首先我们来看第一件事 也就是 getDecorView() , Window 是一个抽象类,而 Window 的唯一实现类 是 com.android.internal.policy.PhoneWindow所以我们就看一下 PhoneWindow 是怎么实现的。

PhoneWindow 的 getDecorView()实现

PhoneWindowgetDecorView() 很显然这个方法是个单例的,只会创建一个 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() 方法比较长,但实际只干了两件事:

  1. 如果mDecor 为空则调用 generateDecor() 创建 DecorView 对象
  2. 如果 mContentParent 为空则调用 generateLayout() 创建ContentParent 对象
generateDecor() 创建DecorView

generateDecor() 直接创建了一个新的 DecorView

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值