前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >setContentView流程

setContentView流程

作者头像
全栈程序员站长
发布于 2022-09-13 03:26:09
发布于 2022-09-13 03:26:09
59600
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

1、activity、window、DecorView、ViewRoot之间的预备知识

activity

activity是Android的四大组件之一,负责控制activity的生命周期和处理事件,负责视图的添加与显示,以及通过一些回调方法与window和View进行交互。一个activity包含一个window,window才是真正的窗口

Window

Window是一个抽象类,它真正的实现类是PhoneWindow。Window通过WindowManager加载一个DecorView到Window中,并将DecorView交给ViewRoot。 FrameWork定义了三种窗口类型,三种类型定义在WindowManager,通过LayoutParams.type设置

  • 应用窗口,对应于一个Activity。加载Activity由AmS完成,创建一个应用窗口只能在Activity内部完成(层级1~99)。
  • 子窗口,必须依附于任何类型的父窗口(层级1000~1999)。
  • 系统窗口,不需要对应任何Activity,如:状态栏,导航栏,普通应用程序不能创建系统窗口,必须要有系统应用权限.(层级2000~2999)。

DecorView

DecorView是FrameLayout的子类,它可以被认为是Acitivity的视图根节点。是setContentView所设置的View的父容器

ViewRoot

ViewRoot对应ViewRootImp类,它是连接WindowManager和DecorView的纽带,在ActivityThread中,当Activity对象创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImp对象,并将ViewRootImp对象和DecorView建立关联。View的三大流程measure layout draw都是通过ViewRoot完成。ViewRoot并不属于View树的一部分,从源码上看它既非View的子类,也非View的父类,但是它实现了ViewParent接口,所以可以算作名义上的View的父视图。ViewRoot继承了Handler类,Android所有的触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的. ViewRootImpl中调用performTraversals方法,然后便开始测量布局绘画了,界面才得以显示出来,这就是View的绘制流程起点。

2、activity、window、decorView的视图层级关系

看下一张图

上图描述了activity、window、decorView和设置View的视图层级关系

3、setContentView的具体流程源码

先从Activity.java的setContentView()开始

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看到如下步骤:

  • 获取window(PhoneWindow)
  • 调用PhoneWindow的setContentView方法。
  • 初始化dcorView的ActionBar(3.0推出,目的是为了统一界面风格,现在已经被5.0出的ToolBar取代)

PhoneWindow.java的setContentView过程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void setContentView(int layoutResID) {
      
        if (mContentParent == null) {
             //安装
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //生成的布局内容添加到 decoView的contentView中去
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        //...
    }

上面的方法中主要就是干了2件事:

  • 初始化安装DecorView
  • 将布局内容通过LayoutInflatert添加到decorView的Content中去。

installDecor()

下面继续看installDecor()方法干了什么

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //创建decorView
            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) {
            //为decorView设置布局样式,并返回mContentParent
            mContentParent = generateLayout(mDecor);

          ....
        }
    }
1、generateDecor()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, this);
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        //创建DecorView 
        return new DecorView(context, featureId, this, getAttributes());
    }

generateDecor()的方法很简单,就是获取上下文context,并且实例化DecorView.

2、generateLayout()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    protected ViewGroup generateLayout(DecorView decor) {
        // 现货区当前主题
        TypedArray a = getWindowStyle();

        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
            getAttributes().setFitInsetsSides(0);
            getAttributes().setFitInsetsTypes(0);
        }
        //设置窗口特征,
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

        ........
        // Inflate the window decor.
        //根据主题样式,加载布局
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        //将适配的布局文件生成root,并且调用addView的方法添加到decorview中去
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
       //拿到content通过布局,注意该id的值,获取的就是mContentParent
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            mDecor.setWindowBackground(mBackgroundDrawable);

            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);

            if (mTitle != null) {
                setTitle(mTitle);
            }

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

上面的方法简单的流畅描述下:

  • 先获取当前window的主题样式
  • 根据主题样式找到对象的布局
  • 根据布局样式加载对应的布局到decorView中去
  • 然后通过findViewByid的方法获取到View,返回View即为mContentParent。

注意: 拿到content的过程。上面通过主题加载布局,此次使用R.layout.screen_simple,作为例子来操作下面获取contentParent的过程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
  
   //拿到content
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

screen_simple不布局内容

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <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:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

改文件内容很简单一个是ViewStub,此用来是设置actionBar的设置,下面一个FrameLayout对应的id是content,所以 mContentParent的就是framelayout。

到此setContentView的流程大致已经结束。以上是decorView已经创建起来。注意此时的DecorVIew还是不可见的。

4、DecorView的显示

当DecorView的构造流程完成时此时decorView还没有添加到window中。 ActivityThread的handleResumeActivity方法中,首先会调用Activity的onResume方法,接着调用Activity的makeVisible()方法。 makeVisible()中通过WindowManager.addView()完成了DecorView的添加和显示两个过程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 void makeVisible() { 
   
        if (!mWindowAdded) { 
   
            ViewManager wm = getWindowManager();
            //windowManager添加DecorView
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        //设置decorView可见
        mDecor.setVisibility(View.VISIBLE);
    }

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/153247.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
yum 安装 nginx
我之前在nginx的官方文档中看到使用yum安装php方法。觉得如果有嫌编译麻烦的。可以尝试使用yum快速安装nginx。减少搭建环境之苦。
魔王卷子
2019/05/28
9.9K3
Nginx 一个牛X的功能,流量拷贝!
为了实现流量拷贝,Nginx提供了ngx_http_mirror_module模块
良月柒
2020/03/12
9850
Nginx 一个牛X的功能,流量拷贝!
CentOS 7安装Nginx简明教程
配置Nginx仓库 执行命令: vi /etc/yum.repos.d/nginx.repo 添加如下内容: [nginx] name=nginx repo baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/ gpgcheck=0 enabled=1 更新仓库索引并安装Nginx yum update yum install nginx 开机启动Nginx systemctl enable nginx 启动Nginx sudo
DevOps持续交付
2021/01/28
3330
centos8.0下安装Nginx1.17.1
#安装yum工具包 sudo yum install yum-utils #构建链接 vim /etc/yum.repos.d/nginx.repo # i 进入insert状态 # 把下面内容复制粘贴进去 [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/key
诡途
2022/05/09
1470
centos8.0下安装Nginx1.17.1
Nginx:nginx在centos下的安装及网关配置
Freedom123
2024/03/29
3250
基于centos7安装部署nginx
WEB服务器主要功能是提供网上信息浏览服务。目前我们上网浏览信息,都是由不同的web服务器提供的,企业通常都会有自己的门户网站,所以对于web服务器的掌握也是至关重用。下面我们可以熟悉下常见的web服务器有哪些。
运维朱工
2021/11/28
1.3K2
基于centos7安装部署nginx
【实践】使用Nginx作为GrayLog日志接入的负载均衡
在Graylog集群环境上,日志源将日志发送到负载均衡,由负载均衡来分配日志发送到GrayLog具体哪个节点上,实现各节点接入能力和处理能力平衡均分
yuanfan2012
2023/06/23
5560
【实践】使用Nginx作为GrayLog日志接入的负载均衡
Ngin服务学习(3)-yum安装ngi
cat /etc/redhat-release 查看系统版本 uname -a 查看系统内核详情
py3study
2020/01/17
4280
Linux下使用yum安装LNMP环境
如何使用yum搭建有需求的LNMP环境 本人使用的是centos7系统,就以centos7的系统搭建为例 Nginx安装 下载/安装步骤:http://nginx.org/en/linux_packages.html#stable 创建/编辑文件:vi /etc/yum.repos.d/nginx.repo 编辑内容:OS代表系统 OSRELEASE 代表系统版本号 [nginx] name=nginx repo baseurl=http://nginx.org/packages/OS/O
北溟有鱼QAQ
2019/12/18
3K0
Linux安装nginx
指剑
2023/05/31
1.3K0
Linux安装nginx
centos7安装Lnmp(Linux+Nginx+MySql+Php)及Apache
Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,Nginx是一个高性能的HTTP和反向代理服务器,Nginx 超越 Apache 的高性能和稳定性,使得国内使用 Nginx 作为 Web 服务器的网站也越来越多,我们学习PHP,以及搭建我们自己的LNMP环境,不妨先在本机上尝试学习,下面我们一步一步来完成在CentOS7 下安装LNMP(Linux+Nginx+MySQL+PHP)及Apache。
botkenni
2019/09/03
1.5K0
centos7安装Lnmp(Linux+Nginx+MySql+Php)及Apache
Centos7安装Nginx详细安装步骤
说明:centos系统中默认的yum仓库中没有nginx的安装包, 所以要想安装nginx需要单独指定它的仓库地址
全栈程序员站长
2022/08/13
4.7K0
Centos7安装Nginx详细安装步骤
CentOS 7 安装 Nginx, PHP,MySQL 套件
源地址: nginx: Linux packages 创建 vi /etc/yum.repos.d/nginx.repo , 并且填充以下内容来安装 yum repository 库
hedeqiang
2019/12/18
2K0
CentOS7系统Yum安装Nginx
配置Nginx源 cat >/etc/yum.repos.d/nginx.repo<<EOF [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true [nginx-mainlin
院长技术
2020/12/10
7130
利用 Nginx 反向代理搭建本地 yum 服务器
在政府,医院等单位有网络安全要求,对内外网进行物理隔离,然而内网主机无法访问互联网下载安装包,通过Nginx 反向代理搭建本地yum服务器实现内网主机安装包下载。
Kevin song
2020/10/29
1.6K0
利用 Nginx 反向代理搭建本地 yum 服务器
CentOS使用yum安装Nginx
如何在CentOS安装Nginx,在网上搜了一圈,大部分都是下载源代码进行编译,感觉不太方便。查看了一下官方文档,其实官方是有提供安装源的。
Mr. Wei
2021/01/16
1.4K0
在Centos7.3搭建Yum私有仓库
使用本地iso镜像创建本地yum仓库,该方法不推荐,只针对yum服务器无法上公网的环境下操作,毕竟iso镜像里的包非常有限。
菲宇
2019/06/12
2.1K0
在Centos7.3搭建Yum私有仓库
Yum安装nginx
1、先下载前置包 yum install yum-utils 报错的话看最下边 2、进入文件配置信息 cd /etc/yum.repos.d/ vim nginx.repo 输入以下信息 [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_sig
高大北
2022/06/14
5.3K0
Yum安装nginx
CentOS 6 添加常用 yum 源 转
CentOS 的官方源去掉了一些与版权有关的软件,因此想要安装这些软件或者手动下载安装,或者使用其他源. 下面我推荐常用的两个源, 这两个源基本可以满足一般服务器的使用需求.
双面人
2019/04/10
2.8K0
CentOS6报cannot found a valid baseurl for repo:base
最近需要用一下centos6版本,发现使用yum时报cannot found a valid baseurl for repo:base 先说原因: 2020年12月2日官方结束了对CentOS 6.x的支持,导致使用yum安装或更新程序的时候出现错误 解决方案: 清空原有CentOS-Base.repo文件 echo > /etc/yum.repos.d/CentOS-Base.repo 编辑CentOS-Base.repo文件, 输入以下内容: vi /etc/yum.repos.d/Cen
流柯
2021/06/24
1.6K0
相关推荐
yum 安装 nginx
更多 >
LV.1
腾讯科技运营开发
目录
  • 1、activity、window、DecorView、ViewRoot之间的预备知识
    • activity
    • Window
    • DecorView
    • ViewRoot
  • 2、activity、window、decorView的视图层级关系
  • 3、setContentView的具体流程源码
    • PhoneWindow.java的setContentView过程
      • installDecor()
  • 4、DecorView的显示
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档