Activity、Window、DecorView的关系

目录

一、Activity、Window、DecorView的层级关系如下图所示:

1、Activity

2、Window

3、DecorView   

二、DecorView初始化相关源码  

三、DecorView显示时机


前言: 不同的Android版本有差异,以下基于Android 11进行讲解。

一、Activity、Window、DecorView的层级关系如下图所示:

        从上图可以直观的看到Activity、Window以及DecorView之间的关系,Activity持有window(Phone Window)、而在window里管理着DecorView,我们一般平时开发对Activity样式的修改,实际上是对DecorView的修改、系统中提供着几种默认DecorView样式。 

 1、Activity

对于应用的开发,我们通常操做Activity来创建我们想要的视图界面,但实际上对视图的控制并不是Activity,而是Activity持有的Window。每一个Activity包含一个PhoneWindow,PhoneWindow是window的子类。

如上图所示,在Activity类中的attach方法中创建了PhoneWindow实例。

2、Window

        Window是视图界面真正的管理器。Window是一个抽象类,具体的实现在PhoneWindow类,PhoneWindow类继承于Window抽象类。PhoneWindow类该持有DecorView的实例。PhoneWindow类通过DecorView来加载布局xml文件。

 如上图所示,在PhoneWindow中给全局变量mDecor负值。mDecor为DecorView实例。

3、DecorView   

 根据以上源码截图我们发现DecorView是FrameLayout类的子类。

Android原生提供了几种样式给DecorView。如下这几种xml布局都是原生提供的:

其中我们看看screen_title.xml布局代码:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:fitsSystemWindows="true"><!-- Popout bar for action modes --><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"android:theme="?attr/actionBarTheme" /><FrameLayoutandroid:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize"style="?android:attr/windowTitleBackgroundStyle"><TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle"android:background="@null"android:fadingEdge="horizontal"android:gravity="center_vertical"android:layout_width="match_parent"android:layout_height="match_parent" /></FrameLayout><FrameLayout android:id="@android:id/content"android:layout_width="match_parent" android:layout_height="0dip"android:layout_weight="1"android:foregroundGravity="fill_horizontal|top"android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

在该布局里,DecorView内部包含一个LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,根据Theme样式设置ActionBar等。中间的是TitleView,有的xml布局没有这一部分,例如screen_simple.xml中就没有这一部分。最下面的是ContentViews,这是最重要的一部分,我们开发应用时在oncreate()函数中调用的setContentView()加载方法,其实就是将其加载到这个ContentViews里。

    如上图所示,phonewindow中通过DecorView的实例mDecor.onResourcesLoaded()方法将该布局加载到DecorView.

二、DecorView初始化相关源码  

接下来我们看看从Activity 到 PhoneWindow再到DecorView的初始化以及布局加载源码:

Activity的完整启动流程在这里不再细说,直接从ActivityThread.java的handleLaunchActivity()方法开始讲解。代码如下:

\frameworks\base\core\java\android\app\ActivityThread.java

@Overridepublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {.................// Hint the GraphicsEnvironment that an activity is launching on the process.GraphicsEnvironment.hintActivityLaunch();final Activity a = performLaunchActivity(r, customIntent);//(1).................return a;}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {..................appContext.getResources().addLoaders(app.getResources().getLoaders().toArray(new ResourcesLoader[0]));appContext.setOuterContext(activity);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); //(2)
..................}

如上代码,在Activity的启动过程中,其中在ActivityThread.java中的与PhoneWindow及DecorView初始化相关部分得调用流程:

handleLaunchActivity()=> performLaunchActivity( ) => activity.attach( )

接下来我们看看一下activity中attach()方法:

frameworks\base\core\java\android\app\Activity.java

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){...................mWindow = new PhoneWindow(this, window, activityConfigCallback); //(3)mWindow.setWindowControllerCallback(mWindowControllerCallback);mWindow.setCallback(this);     //(4)...................}

在注释(3)可以看到Activity持有PhoneWindow并初始化了PhoneWindow实例,在注释(4)中调用PhoneWindow的setCallback方法将activity实例设置给PhoneWindow,这一点很重要,我们在看DecorView的代码时候,看到的mWindow.getCallback()方法,实际就是获取DecorView相对应的Activity实例。

我们在第一节第3点讲DecorView的布局的时候讲到在oncreate()方法中调用setContentView()方法给activity设置布局是加载到DecorView的ContentViews里。接下来我们看一下这个代码流程:

frameworks\base\core\java\android\app\Activity.java

    public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}

我们在前面注释(3)中讲了Activity持有PhoneWindow并初始化了PhoneWindow实例mWindow。

public Window getWindow() {return mWindow;}

getWindow()是拿到我们初始化好的PhoneWindow。

我们看一下PhoneWindow中的setContentView()方法:

frameworks\base\core\java\com\android\internal\policy\PhoneWindow.java

    @Overridepublic 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) { //(5)installDecor();  //(6)} 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);//(7)}mContentParent.requestApplyInsets();final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet = true;}

以上,注释(3)中的mContentParent 就是DecorView的ContentViews,在(6)中,如果mContentParent为空即通过installDecor()方法初始化一个DecorView实例、并将此PhoneWindow实例传给DecorView,installDecor( )方法如下:

private void installDecor() {............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);}............
}

在注释(7)中将xml布局文件加载给DecorView的ContentViews。

总结:

Activity创建PhoneWindow对象,并把自己实例传递给PhoneWindow;PhoneWindow会创建一个DecorView对象,并把自己实例传递给DecorView。DecorView创建过程中根据开发者设置的不同的主题,加载不同的布局到DecorView的ContentViews里。

三、DecorView显示时机

根据我们平时的开发经验,我们给activity设置的布局,在activity调用了onResume()方法之后才会显示出来,那么接下来我们看一下这个流程是否是我们想的这样。

在以上我们详述onCreate()方法中调用了setContentView()方法给DecorView设置了布局,此时并不可见。Activity的onResume()方法的调用以及DecorView的显示都会在ActivityThread的handleResumeActivity()方法实现,我们看一下该方法:

\frameworks\base\core\java\android\app\ActivityThread.java

    @Overridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {.....................final ActivityClientRecord r = performResumeActivity(token, 
finalStateRequest, reason); //(8)if (r == null) {// We didn't actually resume the activity, so skipping any follow-up actions.return;}.....................if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);  //(9)ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;l.softInputMode |= forwardBit;.....................if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;wm.addView(decor, l); //(10)} else {a.onWindowAttributesChanged(l);}}} else if (!willBeVisible) {if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");r.hideForNow = true;}if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {.....................if (r.activity.mVisibleFromClient) {r.activity.makeVisible(); //(11)}}.....................}

 我们先看注释(8)performResumeActivity()方法的调用。

\frameworks\base\core\java\android\app\ActivityThread.java

  public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,String reason){final ActivityClientRecord r = mActivities.get(token);.................r.activity.performResume(r.startsNotResumed, reason);//(12).................}

注释(12)中,调用了activity的performResume()的方法:

\frameworks\base\core\java\android\app\Activity.java

 final void performResume(boolean followedByPause, String reason) {dispatchActivityPreResumed();..................mInstrumentation.callActivityOnResume(this);//(13)..................}

注释(13)中,调用了Instrumentation的callActivityOnResume()的方法:

public void callActivityOnResume(Activity activity) {activity.mResumed = true;activity.onResume();//(14).......................}

至此,我们知道了在注释(14)中,我们熟悉的activity中的onResume()方法是被Instrumentation的callActivityOnResume()方法所调起的。

回到前面,也就是说在ActivityThread.java中的注释(8)中的performResumeActivity()方法里就调起了activity中的onResume()方法。

再看注释(9)decor.setVisibility(View.INVISIBLE),将DecorView设置为不可见,注释(10)中wm.addView(decor, l)通过addView的方式将decor视图添加;最后在注释(11)r.activity.makeVisible()方法里将decor设置为VISIBLE。makeVisible()方法如下:

\frameworks\base\core\java\android\app\Activity.java

   void makeVisible() {if (!mWindowAdded) {ViewManager wm = getWindowManager();wm.addView(mDecor, getWindow().getAttributes());mWindowAdded = true;}mDecor.setVisibility(View.VISIBLE);}

总结:由以上代码流程我们知道,activity里的decorView的布局的显示是在activity中的OnResume()执行了之后才展示出来的,但是我们需要注意的是,即使我们发现了在activity中的OnResume()被调用了,但是handleResumeActivity()方法中的r.activity.makeVisible()方法没有被执行,该布局视图依然是不可见的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/39005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Halide AOT模式

这种模式会提前&#xff0c;会提前编译好&#xff0c;变成dll什么的&#xff0c;可接受任何输入的参数运行。 然后这样调用&#xff0c;必须要make一下前一个file&#xff0c;才有后面的.h

魔行观察-AI数据分析-蜜雪冰城

摘要 本报告旨在评估蜜雪冰城品牌作为投资对象的潜力和价值&#xff0c;基于其经营模式、门店分布、人均消费、覆盖省份等关键指标进行分析。 数据数据源&#xff1a;魔行观察&#xff1a;http://www.wmomo.com/#/brand/brandDetails?code10013603 品牌概览 蜜雪冰城是中国…

Vue 爬坑

都是基于最新的Vue3版本 "vue": "^3.4.29" 1 vue组建样式设置 <script setup lang"ts"> import HelloWorld from ./components/HelloWorld.vue </script><template><div><a href"https://vitejs.dev" tar…

RPA 第一课

RPA 是 Robotic Process Automation 的简称&#xff0c;意思是「机器人流程自动化」。 顾名思义&#xff0c;它是一种以机器人&#xff08;软件&#xff09;来替代人&#xff0c;实现重复工作自动化的工具。 首先要说一句&#xff0c;RPA 不是 ChatGPT 出来之后的产物&#x…

elementui中@click短时间内多次触发,@click重复点击,做不允许重复点击处理

click快速点击&#xff0c;发生多次触发 2.代码示例&#xff1a; //html<el-button :loading"submitLoading" type"primary" click"submitForm">确 定</el-button>data() {return {submitLoading:false,}}//方法/** 提交按钮 */sub…

分布式锁——基于Redis分布式锁

单机锁 服务器只有一个&#xff0c;JVM只有一个。 用synchronized加锁&#xff0c;对lock对象加锁&#xff0c;只有线程1结束&#xff0c;线程2,3才会开始。 再用uid避免一个线程多次进来。 分布式锁 真正上线时&#xff1a; 【注&#xff1a;这些服务器连接的是一个Redis集…

STM32入门笔记(03): ADC(SPL库函数版)(2)

A/D转换的常用技术有逐次逼近式、双积分式、并行式和跟踪比较式等。目前用的较多的是前3种。 A/D转换器的主要技术指标 转换时间 分辨率 例如&#xff0c;8位A/D转换器的数字输出量的变化范围为0&#xff5e;255&#xff0c;当输入电压的满刻度为5V时&#xff0c;数字量每变化…

如何学好自动化测试

1. 什么是自动化测试 自动化测试是使用脚本和工具来执行测试任务&#xff0c;以替代手工测试过程。它可以提高效率、减少人工错误&#xff0c;并增加测试覆盖率。在软件开发过程中&#xff0c;自动化测试已经成为了不可或缺的一部分。 自动化测试主要有以下好处&#xff1a; …

Amos结构方程模型---探索性分析

初级 第5讲 探索性分析_哔哩哔哩_bilibili amos中基本操作&#xff1a; 椭圆潜变量&#xff0c;不可预测 数据导入 改变形状 判定系数 方差估计和假设检验&#xff1a; 探索性分析&#xff1a; ses&#xff08;潜变量&#xff09;社会经济指数 从考虑最大的MI开始&#xff0c;卡…

【Python画图-驯化seaborn】一文搞懂seaborn中的箱线图实践技巧

【Python画图-驯化seaborn】一文搞懂seaborn中的箱线图实践技巧 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档关注&a…

1.4 ROS2集成开发环境搭建

1.4.1 安装VSCode VSCode全称Visual Studio Code&#xff0c;是微软推出的一款轻量级代码编辑器&#xff0c;免费、开源而且功能强大。它支持几乎所有主流的程序语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比Diff、GIT 等特性&#xff0c;支持插件…

7.3数据库第一次作业

安装MySQL 1.打开安装包 2.选择自定义安装&#xff08;custom&#xff09;并点击下一步 3.自定义安装路径 4.点击执行 5.执行成功 6.默认选项点击下一步 7.选择新的授权方式并点击下一步 8.配置密码 9.默认配置并点击下一步 10.点击执行&#xff08;Execute&#xff09; 11.执…

python中的文件

1.什么是文件&#xff1f; 硬盘上存储的数据都是以文件的形式来组织的~ 文件是数据在硬盘上的存储形式&#xff0c;不同的数据在硬盘上的存储形式是不同的&#xff0c; 2.文件路径 文件夹/目录。 文件夹&#xff0c;再包含文件夹的情况&#xff0c;这就是一个嵌套的关系&…

2024-2025年本田维修电路图线路图接线图资料更新

此次更新了2024-2025年本田车系电路图资料&#xff0c;覆盖市面上99%车型&#xff0c;包括维修手册、电路图、新车特征、车身钣金维修数据、全车拆装、扭力、发动机大修、发动机正时、保养、电路图、针脚定义、模块传感器、保险丝盒图解对照表位置等等&#xff01; 汽修帮手汽…

Java中使用arima预测未来数据

看着已经存在的曲线图数据&#xff0c;想预估下后面曲线图的数据。 import java.util.Vector;public class AR {double[] stdoriginalData{};int p;ARMAMath armamathnew ARMAMath();/*** AR模型* param stdoriginalData* param p //p为MA模型阶数*/public AR(double [] stdori…

你的硬盘知道的太多:你以为你的秘密真的被删除了吗?

某一天你收到了朋友发给你的一个秘密文件&#xff0c;在看完之后&#xff0c;为了不被别人发现&#xff0c;你决定将文件毁尸灭迹&#xff01; 你选中文件名称 / 右键 / 删除&#xff0c;好了&#xff0c;文件已经消失了。但你是懂电脑的&#xff0c;知道文件此时还在回收站里面…

海外虚拟卡开卡平台有哪些?无限开卡,无其他限制

随着时代的发展很多小伙伴都需要海外虚拟卡&#xff0c;海外虚拟卡开卡平台我这里用的是Fomepay的&#xff0c;他们比较人性化&#xff0c;有客服&#xff0c;随时可咨询 对于消费者而言&#xff0c;虚拟卡号提供了隐私&#xff0c;因此广告商更难以跟踪和定位购买行为&#x…

《python程序语言设计》2018版第5章第50题利用turtle编程显示三角形图案

2024.06.18 05.50.01version 首先我觉得还是应该现从简单阵列来进行。非常简单。顺便回忆一下我3月份做的5.19题里那些淘气的数列 代码成功 #将i从10设计成12打印的毕竟好看 for i in range(1,12):#这这里给结尾的i2效果并不好看for j in range(1,i):print(j,end" "…

【深度学习】Transformer

李宏毅深度学习笔记 https://blog.csdn.net/Tink1995/article/details/105080033 https://blog.csdn.net/leonardotu/article/details/135726696 https://blog.csdn.net/u012856866/article/details/129790077 Transformer 是一个基于自注意力的序列到序列模型&#xff0c;与基…

软件测试与质量保证 | 云班课选择题库

目录 第1章课后习题 第2章课后习题 第3章课后习题 第4章课后习题 第5章课后习题 第6章课后习题 第7章课后习题 第8章课后习题 第9章课后习题 第10章课后习题 第11章课后习题 第12章课后习题 第13章 测试相关未分类习题 第1章课后习题 1. 与质量相关的概念包括 &a…