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" />
    <FrameLayout
        android: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

@Override
    public 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

    @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) { //(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

    @Override
    public 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.mfbz.cn/a/767160.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

音乐发行平台无加密开源源码

适用于唱片公司&#xff0c;用于接收物料&#xff0c;下载物料功能&#xff1a;个人或机构认证&#xff0c;上传专辑和歌曲&#xff0c;版税结算环境要求php7.4Nginx 1、导入数据库 2、/inc/conn.php里填写数据库密码等后台路径/admin&#xff08;可自行修改任意入口名称&…

Meta 3D Gen:文生 3D 模型

是由 Meta 公布的一个利用 Meta AssetGen&#xff08;模型生成&#xff09;和 TextureGen&#xff08;贴图材质生成&#xff09;的组合 AI 系统&#xff0c;可以在分分钟内生成高质量 3D 模型和高分辨率贴图纹理。 视频演示的效果非常好&#xff0c;目前只有论文&#xff0c;期…

计算机网络--网络层

一、网络层的服务和功能 网络层主要为应用层提供端对端的数据传输服务 网络层接受运输层的报文段&#xff0c;添加自己的首部&#xff0c;形成网络层分组。分组是网络层的传输单元。网络层分组在各个站点的网络层之间传输&#xff0c;最终到达接收方的网络层。接收方网络层将运…

PLC_博图系列☞TP:生成脉冲

PLC_博图系列☞TP&#xff1a;生成脉冲 文章目录 PLC_博图系列☞TP&#xff1a;生成脉冲背景介绍TP&#xff1a; 生成脉冲说明参数脉冲时序图示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 TP 背景介绍 这是一篇关于PLC编程的文章&#xff0c;特别是关于西门…

快速上手文心一言指令:解锁AI对话新纪元

快速上手文心一言指令 一、引言&#xff1a;文心一言的魅力所在二、准备工作&#xff1a;了解文心一言平台2.1 轻松注册&#xff0c;开启智能对话之旅2.2 深度探索&#xff0c;掌握界面布局奥秘2.2.1 输入框&#xff1a;智慧交流的起点2.2.2 回复区&#xff1a;即时反馈的窗口2…

Echarts-折线图

1.案例1 1.1代码 option {"tooltip": {"trigger": "axis","backgroundColor": "rgba(32, 33, 36,.7)","borderColor": "rgba(32, 33, 36,0.20)","borderWidth": 10,"textStyle"…

星辰资讯 | TiUP v1.16 发版,支持 PD 微服务

如果你对 TiDB 还不太了解&#xff0c;或者你对数据库安装部署的认知仍然停留在手动和脚本时代&#xff0c;那么&#xff0c;请先戳这里了解一下 TiUP 神器&#xff1a; 震惊&#xff01;数据库小白装国产数据库只需10分钟&#xff01; TiDB 7.x 源码编译之 TiUP 篇 TiUP&#…

基于改进高斯-拉普拉斯滤波器的一维时间序列平滑与降噪(MATLAB)

以图像处理为例&#xff0c;拉普拉斯算子是基于图像的二阶导数来找到边缘并搜索过零点&#xff0c;传统的拉普拉斯算子常产生双像素宽的边缘&#xff0c;对于较暗区域中的亮斑进行边缘检测时&#xff0c;拉普拉斯运算就会使其变得更亮。因此&#xff0c;与梯度算子一样&#xf…

基于tensorflow2的目标检测完整实现过程

序言 虽然tf1仍然在维护&#xff0c;但tf2毕竟是主流&#xff0c;如果不是项目有明确要求&#xff0c;建议直接选择tf2。本文以tf2为例展开&#xff0c;总结从环境准备到使用自己的数据和tensorflow预训练模型进行快速训练和调用。对tensorflow和目标检测算法有深入了解的&…

Seal^_^【送书活动第8期】——《ChatGLM3大模型本地化部署、应用开发与微调》

Seal^_^【送书活动第8期】——《ChatGLM3大模型本地化部署、应用开发与微调》 一、参与方式二、本期推荐图书2.1 作者建语2.2 编辑推建2.3 图书简介2.4 前 言2.5 目 录 三、正版购买 大模型领域 既是繁星点点的未知宇宙&#xff0c;也是蕴含无数可能的广阔天地&#xff0c; 正…

“不喝鸡汤 不诉离殇”华火电燃灶用实力引领烹饪灶具发展

在这个快节奏的时代&#xff0c;我们常常被各种厨房电器的鸡汤所包围&#xff0c;并悄悄的告诉我们厨房生活是美好与温暖的&#xff0c;但面对现实中的挑战与困难时&#xff0c;常常表现出选择性失明&#xff1b;那些隐藏在传统厨房烹饪环境下的危机&#xff0c;就像是慢性的毒…

参数污染漏洞(HPP)挖掘技巧及实战案例全汇总

目录 概念: 漏洞原理: 实战案例总结: 1. 逻辑漏洞(IDOR) 2. 绕过检测(WAF) 挖掘技巧: 修复方案: 概念: HTTP参数污染,也叫HPP(HTTP Parameter Pollution)。简单地讲就是给一个参数赋上两个或两个以上的值,由于现行的HTTP标准没有提及在遇到多个输入值给相…

设计IC行业SAP软件如何处理芯片成本计算

在集成电路(IC)设计与制造行业中&#xff0c;精确的成本计算对于维持健康的财务状况、优化生产流程以及保持市场竞争力至关重要。SAP软件&#xff0c;作为一种全面的企业资源规划(ERP)解决方案&#xff0c;为IC行业提供了强大且灵活的成本计算工具。以下是SAP软件如何处理芯片成…

【Linux】应用层创建XXX文件,文件系统调用可以查看到文件名

搞了台电脑&#xff0c;昨天把系统装了下&#xff0c;继续搞事&#xff1a; 上次基于内核代码openat的系统打印被操作的文件名&#xff0c;发现不成功&#xff0c;很奇怪&#xff0c;这种问题内核不可能会犯这种低级别的问题吧&#xff1f; 反过来想&#xff0c;那不是内核的问…

Vscode快捷键崩溃

Vscode快捷键崩溃 Linux虚拟机下使用vscode写代码【ctrlA&#xff0c;CtrlC&#xff0c;CtrlV】等快捷键都不能使用&#xff0c;还会出现“NO text insert“等抽象的指令&#xff0c;问题就是不知道什么时候装了一个VIM插件&#xff0c;让他滚出电脑》》》

[vue3+js]实现3d旋转效果

1. 实现效果图&#xff1a; 2.实现代码&#xff1a; css: <style lang"scss" scoped>.bottomContainer{width: 1200px;height: 400px;display: flex;justify-content: center;position: relative;margin:200px auto;align-items: center;// background-image…

迈向智慧水利新时代:聚焦智慧水利解决方案的核心技术与发展方向,展望其在推动水利行业可持续发展中的重要作用

目录 一、引言 二、智慧水利解决方案的核心技术 1. 物联网技术 2. 大数据与云计算 3. 人工智能与机器学习 4. 区块链技术 三、智慧水利的发展方向 1. 深化技术融合与创新 2. 强化系统集成与协同 3. 提升公众参与与互动 4. 注重生态友好与可持续发展 四、智慧水利在…

5.3.3、二维数组案例-考试成绩统计

代码 #include <iostream> using namespace std; #include <string>int main() {//二维数组案例-考试成绩统计//1、创建二维数组int scores[3][3] {{100,100,100},{90,50,100},{60,70,80},};string names[3] { "张三","李四","王五&quo…

Qt——升级系列(Level Seven):事件、文件

目录 Qt事件 事件介绍 事件的处理 按键事件 鼠标事件 定时器 事件分发器 事件过滤器 Qt文件 Qt文件概述 输入输出设备类 文件读写类 文件和目录信息类 Qt事件 事件介绍 事件是应⽤程序内部或者外部产⽣的事情或者动作的统称。在 Qt 中使⽤⼀个对象来表⽰⼀个事件。所有的 Qt …

STM32 ADC精度提升方法

STM32 ADC精度提升方法 Fang XS.1452512966qq.com如果有错误&#xff0c;希望被指出&#xff0c;学习技术的路难免会磕磕绊绊量的积累引起质的变化 硬件方法 优化布局布线&#xff0c;尽量减小其他干扰增加电源、Vref去耦电容使用低通滤波器&#xff0c;或加磁珠使用DCDC时尽…