Activity、Window、DecorView的关系

news2024/11/25 18:48:49

目录

一、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.coloradmin.cn/o/1897993.html

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

相关文章

昇思25天学习打卡营第13天|linchenfengxue

Diffusion扩散模型 关于扩散模型&#xff08;Diffusion Models&#xff09;有很多种理解&#xff0c;本文的介绍是基于denoising diffusion probabilistic model &#xff08;DDPM&#xff09;&#xff0c;DDPM已经在&#xff08;无&#xff09;条件图像/音频/视频生成领域取得…

【ARMv8/v9 GIC 系列 5.1 -- GIC GICD_CTRL Enable 1 of N Wakeup Function】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 GIC Enable 1 of N Wakeup Function基本原理工作机制配置方式应用场景小结GIC Enable 1 of N Wakeup Function 在ARM GICv3(Generic Interrupt Controller第三代)规范中,引入了一个名为"Enable 1 of N Wakeup"的功能。…

2024年【湖北省安全员-C证】考试资料及湖北省安全员-C证考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 湖北省安全员-C证考试资料是安全生产模拟考试一点通生成的&#xff0c;湖北省安全员-C证证模拟考试题库是根据湖北省安全员-C证最新版教材汇编出湖北省安全员-C证仿真模拟考试。2024年【湖北省安全员-C证】考试资料及…

7.1作业6

uart4.h #ifndef __UART4_H__ #define __UART4_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_uart.h" //rcc/gpio/uart4初始化 void hal_uart4_init(); //发送一个字符 void hal_put_char(const char s…

探索Sui的面向对象模型和Move编程语言

Sui区块链作为一种新兴的一层协议&#xff08;L1&#xff09;&#xff0c;采用先进技术来解决常见的一层协议权衡问题。Cointelegraph Research详细剖析了这一区块链新秀。 Sui使用Move编程语言&#xff0c;该语言专注于资产表示和访问控制。本文探讨了Sui的对象中心数据存储模…

滑动窗口(C++)

文章目录 1、长度最小的子数组2、无重复字符的最长子串3、最大连续1的个数 Ⅲ4、将x减到0的最小操作数5、水果成篮6、找到字符串中所有字母异位词7、串联所有单词的子串8、最小覆盖子串 通常&#xff0c;算法的主体说明会放在第一道题中。但实际上&#xff0c;不通常。 算法在代…

Solr安装IK中文分词器

Solr安装IK中文分词器 如何安装Solr与导入数据&#xff1f;为什么要安装中文分词器下载与安装IK分词器1.1、下载IK分词器1.2、安装IK  第一步&#xff1a;非常简单&#xff0c;我们直接将在下的Ik分词器的jar包移动到以下文件夹中  第二步&#xff1a;修改Core文件夹名下\c…

代理设计模式和装饰器设计模式的区别

代理设计模式: 作用:为目标(原始对象)增加功能(额外功能,拓展功能) 三种经典应用场景: 1&#xff1a;给原始对象增加额外功能(spring添加事务,Mybatis通过代理实现缓存功能等等) 2&#xff1a;远程代理&#xff08;网络通信&#xff0c;输出传输&#xff08;RPC&#xff0c;D…

Motion Guidance: 扩散模型实现图像精确编辑的创新方法

在深度学习领域&#xff0c;扩散模型&#xff08;diffusion models&#xff09;因其能够根据文本描述生成高质量图像而备受关注。然而&#xff0c;这些模型在精确编辑图像中对象的布局、位置、姿态和形状方面仍存在挑战。本文提出了一种名为“运动引导”&#xff08;motion gui…

图书馆数据仓库

目录 1.数据仓库的数据来源为业务数据库&#xff08;mysql&#xff09; 初始化脚本 init_book_result.sql 2.通过sqoop将mysql中的业务数据导入到大数据平台&#xff08;hive&#xff09; 导入mysql数据到hive中 3.通过hive进行数据计算和数据分析 形成数据报表 4.再通过sq…

如何取消闪迪Micro SD卡的写保护?这个技巧很有效!

由于受写保护影响&#xff0c;无法格式化闪迪Micro SD卡&#xff1f;别担心&#xff01;通过本文你可以学习如何解除闪迪Micro SD卡的写保护。 我的闪迪SD卡有写保护怎么办&#xff1f; “我打算格式化我的闪迪SD卡。但当我进行格式化时&#xff0c;提示我磁盘被写保护。我想用…

Linux配置固定ip地址

虚拟机的Linux操作系统&#xff0c;其IP地址是通过DHCP服务获取的 DHCP&#xff1a;动态获取IP地址&#xff0c;即每次重启设备后都会获取一次&#xff0c;可能导致IP地址频繁变更。 一般系统默认的ip地址设置都是自动获取&#xff0c;故每次系统重启后ip地址都可能会不一样&a…

数字化产科管理平台全套源码,java产科电子病历系统源码

数字化产科管理平台全套成品源码&#xff0c;产科电子病历系统源码&#xff0c;多家大型妇幼专科医院应用案例。源码完全授权交付。 数字化产科管理平台&#xff08;智慧产科系统&#xff09;是为医院产科量身定制的信息管理系统。它管理了孕妇从怀孕开始到生产结束42天以内的一…

欢乐钓鱼大师攻略:西沙群岛攻略,内置自动辅助云手机!

《欢乐钓鱼大师》是一款以钓鱼为主题的休闲游戏&#xff0c;玩家可以在虚拟的钓鱼世界中体验真实的钓鱼乐趣&#xff0c;并通过捕捉各种珍稀鱼类来提升自己的钓鱼技能和成就。在这篇攻略中&#xff0c;我们将重点介绍如何在西沙群岛区域有效地捕捉各种典藏鱼类&#xff0c;并提…

数据结构之顺序表专题

在学习数据结构之前我们要先了解什么是数据结构&#xff1f; 1.数据结构相关概念 1.什么是数据结构&#xff1f; 数据结构是由“数据”和“结构”两词组合而来。 什么是数据?常见的数值1、2、3、4.、教务系统里保存的用户信息(姓名、性别、年龄、学历等等)、网页里肉眼可以…

Qt项目:基于Qt实现的网络聊天室---注册模块

文章目录 基本页面设计创建登录界面创建注册界面优化样式完善注册类界面 客户端逻辑完善客户端增加post逻辑客户端配置管理 邮箱注册服务认证服务读取配置邮箱验证服务联调设置验证码过期封装redis操作类封装redis连接池注册功能Server端接受注册请求封装mysql连接池封装DAO操作…

传统视觉Transformer的替代者:交叉注意力Transformer(CAT)

传统视觉Transformer的替代者:交叉注意力Transformer(CAT) 在深度学习的世界里,Transformer架构以其在自然语言处理(NLP)领域的卓越表现而闻名。然而,当它进入计算机视觉(CV)领域时,却面临着计算成本高昂和推理速度慢的双重挑战。现在,一项革命性的创新——交叉注意…

【Linux】—VMware安装Centos7步骤

文章目录 前言一、虚拟机准备二、CentOS7操作系统安装 前言 本文介绍VMware安装Centos7步骤。 软件准备 软件&#xff1a;VMware Workstation Pro&#xff0c;直接官网安装。镜像&#xff1a;CentOS7&#xff0c;镜像官网下载链接&#xff1a;https://vault.centos.org/&#x…

[SAP ABAP] 子例程

子例程 示例1 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 示例2 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 补充扩展练习 主程序(Z437_TEST_2024) INCLUDE文件(Z437_TEST_2024_F01) 输出结果如下所示 提示…

使用Rough.js库在画布上绘制一只毛毛虫

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 代码应用场景介绍 本代码使用Rough.js库在画布上绘制一只毛毛虫。 代码基本功能介绍 初始化画布&#xff1a; 使用Rough.js库创建画布&#xff0c;并设置画布尺寸。 绘制毛毛虫身体&#xff1a; 使用椭圆形和…