【15】Android基础知识之Window(一)

news2024/12/29 8:01:36

概述

这篇文章纠结了很久,在想需要怎么写?因为window有关的篇幅,如果需要讲起来那可太多了。从层级,或是从关联,总之不是很好开口。这次也下定决心,决定从浅入深的讲讲window这个东西。

Window

Window是什么,直译是窗口,我们了解过Android应用显示层级就知道,一个应用从下到上分别是:Activity-Window-DecorView-ViewGroup-View,大致是这样的一个层级包裹。可说了这么多,还是没有说清楚window是什么?有一句话是这样说的:Window是视图的容器,视图是Window的内容。
在这里插入图片描述
又打个比方,把视图View比作水,Window比作装水的瓶子,如果没有瓶子,水就不知道放在哪里,根据瓶子的形状,大小,倒入的水就会在瓶子限制的形状,大小内呈现什么样子。这样一讲,是不是就更清楚了点呢。

Window创建

先说一下Activity,在attach方法中,会创建一个window,它是抽象的,创建的是它的实现类PhoneWindow。为什么从Activity说起呢,因为Activity是我们最常见的组件。除了Activity,Dialog和Toast也有他们的window,实现类通常也是PhoneWindow。它们的window创建,可以自行分析一下。

// Activity.java   
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    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,
            IBinder shareableActivityToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        mActivityInfo = info;
		// 创建window
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mAssistToken = assistToken;
        mShareableActivityToken = shareableActivityToken;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
		//设置window的管理者
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    }
Window管理

WindowManager,Window的管理者,上层窗口的管理,都是依赖它,而下层,我们在之后的文章会陆续揭开。从上述贴的aosp中Activity#attach源码可以看到,不仅仅创建window,获取WindowManager,并把它设置到我们创建的window中,也是在attach中进行的。而其中WindowManager是通过系统服务的方式获取的,这种获取的方式,可以认为是一种单例,ContextImpl在执行getSystemService会判断是否已经创建过了WindowManager,否则就创建一个返回。

不过要注意的是,这里的WindowManager也是抽象的概念,它是一个接口,有具体的实现WindowManagerImpl。而比较有趣的事情是,WindowManagerImpl内部有一个单例对象WindowManagerGlobal,关于View的操作都转发给了它去完成的。WindowManagerImpl通过这种代理的方式,让使用更加的灵活,使用者不需要关心具体的窗口管理实现细节,而可以在不同的上下文对象中使用。
在这里插入图片描述
这样一来,看上面这张图就很清晰的了解他们之间的关系了。唯一遗漏的可能就是ViewManager,我们知道WindowManager是一个接口,但是它也同样继承了一个ViewManager的接口类,这个类顾名思义是可以看出来的,对View进行管理。其内部很简单,就是View的添加、更新、删除的接口方法。

// ViewManager.java
public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
     * errors, such as adding a second view to a window without removing the first view.
     * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
     * secondary {@link Display} and the specified display can't be found
     * (see {@link android.app.Presentation}).
     * @param view The view to be added to this window.
     * @param params The LayoutParams to assign to view.
     */
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

不知道有没有注意到,上面我们提到过,WindowManagerGlobal作为WindowManager真正的实现类,它帮助WindowManagerImpl操作的,就是对View的操作,可见ViewManager的接口的具体实现,在WindowManagerGlobal中能找到真正的实现逻辑。

Window和View的关联

回到第一张界面层级图,我们讲了Activity创建了window,讲了window的具体实现,讲了window管理其中的view是通过WindowManager,但是没有讲到DecorView。它在window和view关联中起了重要的作用。

我们知道的DecorView是什么,装饰视图?content view可以理解为内容视图,就是开发的应用界面,title view却不是界面中的标题。而更多理解为是除去应用部分,通用的一些样板界面,比如状态栏,菜单栏等,一些应用都会共有的部分。

解释了DecorView,那它是怎么创建的,可以去PhoneWindow里找找看。我们Activity在设置视图的时候,通常都是通过setContentView来实现的,其中两个重要的点,通过分析源码来介绍一下。

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // 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) {
            //安装 装饰视图
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
        	//添加传入的view
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

installDecor(),创建装饰视图,并且将mDecor设置给当前这个window;判断mContentParent是否为空,为空则创建一个mContentParent并返回。
这里简单的贴一下创建的代码ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

// PhoneWindow.java
private void installDecor() {
        mForceDecorInstall = false;
        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);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

mContentParent#addView,上面看到了ContentParent是一个ViewGroup,在创建的过程中需要传入mDecor,可见ContentParent的约束是依赖mDecor的,即ContentParent是DecorView中的一个ViewGroup。最后通过addView的方法,把内容View添加进去。

梳理一下他们的关联:
window - DecorView - ContentParent - View

总结

1、window是view的容器,view是window的具体内容。
2、window的实现是PhoneWindow。
3、window的管理类是WindowManagerImpl。
4、window把View相关的操作交给了WindowManagerGlobal。
5、window和View的关联是通过DecorView

这样一来,应用层的window,以及相关的管理者,就基本将明白了。但是离我们需要弄明白的事情还有很多,之后会开始分析一个很重要的角色ViewRootImpl

AOSP14源码

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

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

相关文章

django报错(三):No crontab program或got an unexpected keyword argument ‘user’

Crontab是linux系统上的定时管理模块&#xff0c;简单配置&#xff0c;灵活使用。但是要在windows使用必须借助Cygwin等虚拟工具&#xff0c;否则会报错“No crontab program”。如下图&#xff1a; python-crontab是其提供了python模块对crontab的访问&#xff0c;即可以通过p…

【简历】惠州某二本学院:前端简历指导,秋招面试通过率为0

注&#xff1a;为保证用户信息安全&#xff0c;姓名和学校等信息已经进行同层次变更&#xff0c;内容部分细节也进行了部分隐藏 简历说明 这是一份25届二本同学&#xff0c;投递前端职位的简历&#xff0c;那么在校招环节二本同学主要针对的还是小公司&#xff0c;这个学校因为…

怎么关闭Windows安全中心?

Windows安全中心是Windows操作系统中的一项重要功能&#xff0c;系统提供这个功能的目的是保护电脑免受各种安全威胁。尽管如此&#xff0c;有时候我们可能出于某些原因需要关闭它。本文将详细介绍如何关闭Windows安全中心&#xff0c;以及需要注意的事项。 重要提醒&#xff1…

minIO集成springboot

问题 minIO与spring集成。 步骤 创建桶 创建key 找到创建账号页面&#xff0c;如下图&#xff1a; 点击创建&#xff0c;如下图&#xff1a; 设置如下权限&#xff1a; {"Version": "2012-10-17","Statement": [{"Effect": &q…

生成式AI、3D模型交易、模型轻量化、模型格式转换、3D可视化、数字孪生引擎等

老子云3D可视化快速开发平台&#xff0c;集云压缩、云烘焙、云存储云展示于一体&#xff0c;使3D模型资源自动输出至移动端PC端、Web端&#xff0c;能在多设备、全平台进行展示和交互&#xff0c;是全球领先、自主可控的自动化3D云引擎。 平台架构 平台特性 1、基于 HTML5 和 …

django报错(一):python manage.py makemigrations,显示“No changes detected”

执行python manage.py makemigrations命令无任何文件生成&#xff0c;结果显示“No changes detected”。 解决方案一&#xff1a; 1、执行命令&#xff1a;python manage.py makemigrations –empty appname 2、删除其中的0001_initial.py文件&#xff08;因为这个文件内容是…

《昇思25天学习打卡营第25天|第10天》

今天是打卡的第十天&#xff0c;今天开始学应用实践中的LLM原理和实践&#xff0c;今天学的是基于MindSpore实现BERT对话情绪识别。最先了解的是BERT模型的简介&#xff08;来自变换器的双向编码器表征量&#xff08;Bidirectional Encoder Representations from Transformers&…

【Java】:浅克隆和深克隆

克隆 克隆和赋值 克隆的结果是有多个相同的实体&#xff0c;各个对象指向不同的实体而多个不同对象指向一个相同的实体不是克隆&#xff0c;而是赋值 克隆的过程 首先实例化一个 student1 对象 在堆里开辟了一块内存用来存储 age 10 这个数据 调用 clone 方法 在堆中又开辟了一…

Python数据结构:实现自定义栈与队列

更多Python学习内容&#xff1a;ipengtao.com 在计算机科学中&#xff0c;栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;是两种常见的数据结构。它们在算法和数据处理方面有着广泛的应用。本文将详细介绍如何在Python中实现自定义的栈与队列&#xff0c…

stm32精密控制步进电机(升级篇)

这一篇文章里会深入的对步进电机控制方法进行论述 如何避免步进电机丢转的问题 1.机械结构&#xff1a;排查一下传动的问题&#xff0c;举个例子&#xff0c;我的毕设里大臂机械臂的步进电机有时会有丢转问题&#xff0c;造成无法运动到指定位置&#xff0c;后面发现是因为皮带…

【单元测试】SpringBoot

【单元测试】SpringBoot 1. 为什么单元测试很重要&#xff1f;‼️ 从前&#xff0c;有一个名叫小明的程序员&#xff0c;他非常聪明&#xff0c;但有一个致命的缺点&#xff1a;懒惰。小明的代码写得又快又好&#xff0c;但他总觉得单元测试是一件麻烦事&#xff0c;觉得代码…

2.RabbitMQ相关概念

介绍 RabbitMQ是一个消息中间件&#xff0c;接受并转发消息。它接收、存储和转发消息数据。 四大核心概念&#xff1a; 1.生产者 产生数据发送消息的程序是生产者。 2.消费者 3.队列 每一个队列对应一个消费者。 如果两个消费者对应同一个队列&#xff0c;那么队列中的…

云监控(华为) | 实训学习day1(10)

云监控&#xff1a;确保服务器高效运行 在当今的数字化时代&#xff0c;服务器的稳定运行对于任何企业都至关重要。为了确保服务器的 CPU、内存和硬盘等资源的合理运行&#xff0c;云监控成为了一项不可或缺的技术。本文将详细介绍云监控的基本概念、所需软件、配置方法以及如何…

分布式 I/O 系统 BL200 Modbus TCP 耦合器

BL200 耦合器是一个数据采集和控制系统&#xff0c;基于强大的 32 位微处理器设计&#xff0c;采用 Linux 操作系统&#xff0c;支持 Modbus 协议&#xff0c;可以快速接入现场 PLC、SCADA 以及 ERP 系统&#xff0c; 内置逻辑控制、边缘计算应用&#xff0c;适用于 IIoT 和工业…

redis登录缓存

1.pom.xml中引入redis依赖 <!-- Redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency> 2.将登录成功的token存储到redis中 if(Md5…

捷配PCB打样采用机械盲埋孔制造,有何优势?

在电子制造领域&#xff0c;盲孔&#xff08;Blind Vias&#xff09;与埋孔&#xff08;Buried Vias&#xff09;是两种关键的PCB&#xff08;印刷电路板&#xff09;过孔技术。盲孔特指那些连接内层走线至外层走线的过孔&#xff0c;但并不贯穿整个板体。相对地&#xff0c;埋…

【数学建模】技术革新——Lingo的使用超详解

目录 基础知识 1. 变量声明 示例 2. 常量声明 语法格式 示例 3. 目标函数 语法格式 示例 4. 约束条件 语法格式 示例 5. 完整的Lingo模型示例 示例 解释 6. 整数变量声明 语法格式 示例 7. 非线性规划 示例 8. 多目标优化 语法格式 示例 9. 数据输入与…

持续集成02--Linux环境更新/安装Java新版本

前言 在持续集成/持续部署&#xff08;CI/CD&#xff09;的旅程中&#xff0c;确保开发环境的一致性至关重要。本篇“持续集成02--Linux环境更新/安装Java新版本”将聚焦于如何在Linux环境下高效地更新或安装Java新版本。Java作为广泛应用的编程语言&#xff0c;其版本的更新对…

ModuleNotFoundError: No module named ‘_cffi_backend‘的二中情况解决方案

1、问题概述? 创作时间:2024年7月 在pycharm中执行python脚本出现如下问题: No module named _cffi_backend 主要说明二中情况: 第一种原因:最常见的原因就是没有安装cffi模块,我们通过命令安装就可以了。 第二种原因:不常见的原因,如果你在pycharm中运行了别人的…

【自撰写】【国际象棋入门】第10课 常见兵型及其分析

第10课 常见兵型及其分析 兵型是国际象棋棋局当中十分重要的局面因素。好的兵型不仅可以为己方创造空间和布局上的优势&#xff0c;也能够充当进攻或者防守时的有力工具&#xff1b;与此同时&#xff0c;坏的兵型则会削弱己方的优势&#xff0c;并给对方创造进攻的可乘之机。本…