Framework - ActivityThread 应用启动UI渲染流程

一、概念

ActivityThread拥有 main(String[] agrs) 方法,作为程序的入口,是应用程序的初始化类。(ActivityThread不是主线程,它在 main() 方法中实例化,是运行在主线程中。)
ApplicationThread是 ActivityThread 的子类,用作 ActivityThread 与 AMS 进行 BInder 通信的桥梁。
Instrumentation管理 Application 和 Activity 声明周期的类,会在自己对应的方法中传入监听对象,执行该对象的生命周期方法。
ActivityStackActivityThread 中对 Activity 的管理栈,用来记录已经启动的Activity的先后关系、状态信息、是否需要启动新的进程。
ActivityRecord用来映射存储从 AMS 中获取的 Activity 信息的数据类。

二、流程

2.1 应用启动

  1. 点击桌面APP图标时会执行 Launcher.startActivity() ,通过 Binder 通信调用 system_server 进程中 AMS.startActivity() 发起启动请求。
  2. system_server 进程接收到请求后,向 Zygote 进程发送创建进程的请求。
  3. Zygote 进程 fork 出 APP 进程,并执行 ActivityThread.main() 创建主线程、初始化MainLooper、主线程Handler,同时初始化 ApplicationThread 用于和 AMS 通信。
  4. ActivityThread 传递 ApplicationThread 通过 Binder 通信向 AMS 中获取应用信息(即 APP 进程通过 Binder 调用 sytem_server 进程中 AMS 的 attachApplication() 方法)。
  5. AMS 对数据进行一些处理工作后,通过 Binder IPC 向 ActivityThread 发送handleBindApplication 请求(创建启动Application)和 scheduleLaunchActivity 请求(创建启动Activity)。
  6. 在处理 ApplicationThread 回调中返回的应用信息时,通过 Handler 向主线程发送 BIND_APPLICATION 和 LAUNCH_ACTIVITY 两条 Message(这里注意的是 AMS 和主线程并不直接通信,而是 AMS 和主线程的内部类 ApplicationThread 通过 Binder通信,ApplicationThread 再和主线程通过Handler消息交互)。
  7. 主线程在收到 Message 后,反射创建 Application 并执行 onCreate() 生命周期,再通过反射创建 Activity 并执行 onCreate() 生命周期。
  8. 到此 APP 便正式启动,开始进入一系列生命周期 onCreate/onStart/onResume,UI渲染后显示APP主界面。

2.2 UI渲染

2.2.1 onCreate()

2.2.2 onResume()

三、源码分析

3.1 入口 main()

由 Zygote 调用,进程创建后对其进行初始化让APP跑起来,ZygoteInit.zygoteInit() 通过反射找到 ActivityThread.main() 并调用。

public static void main(String[] args) {//主线程Looper的初始化Looper.prepareMainLooper();//实例化ActivityThreadActivityThread thread = new ActivityThread();//启动应用thread.attach(false);//主线程Handler初始化if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}Looper.loop();//一旦上方的主线程loop()循环被终止便抛异常throw new RuntimeException("Main thread loop unexpectedly exited");
}

3.2 初始化主线程Looper

  • 在 Handler 源码中可以看出,初始化主线程 Looper 的参数 quitAllowed 为 false 表示不允许退出,该参数最终会在 MessageQueue 调用 quit() 时进行判断,如果是主线程会抛异常。
  • 主线程 Looper 初始化后会赋值给成员变量 sMainLooper,通过方法 getMainLooper() 向其它线程提供主线程的 Looper 对象。
    //主线程Looper的初始化public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}//普通线程Looper的初始化public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
    public static Looper getMainLooper() {synchronized (Looper.class) {return sMainLooper;}}

3.3 应用启动过程

ActivityThread 向 AMS 中获取信息,AMS将信息封装成对象后回调过来。ActivityThread是逻辑的执行者,AMS是信息的持有者,他们之间通过Binder通信,ApplicationThread是两者通信的桥梁(回调)。

    //ActivityThread中private void attach(boolean system) {//获取系统服务AMS进行Binder通信if (!system) {final IActivityManager mgr = ActivityManager.getService();try {//传入用来通信的桥梁 ApplicationThreadmgr.attachApplication(mAppThread);} ...}}
    //AMS中public final void attachApplication(IApplicationThread thread) {synchronized (this) {attachApplicationLocked(thread, callingPid);}}private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {//启动Applicationthread.bindApplication(...);//启动Activityif (mStackSupervisor.attachApplicationLocked(app)) {...}}

3.3.1 启动 Application

attachApplicationLocked() 中会将 Application 信息赋值给成员变量 ProcessRecord(是一个数据类用来存储 Application 的信息),通过调用 ApplicationThread.bindApplication() 将应用信息回传给 ActivityThread 进行处理。发送 Message 然后 Handler 处理(从Binder线程切换到主线程),调用重写的 handleBindApplication(),通过传过来的 AppBindData 的 makeApplication() 创建 Application 对象,具体是调用 Instrumentation 的newApplication(),通过类加载器类加载器反射加载Application类,执行 onCreate() 生命周期。

  • 如果未指定程序的 Application 则使用默认的 Application 类,否则使用我们指定的。
  • 在创建 Application 对象调用 attachBaseContext() 和 onCreate() 方法之间会调用 ContentProvider的onCreate() 方法这也是很多第三方SDK使用该特性实现初始化的原理。
    //ActivityThread中public final void bindApplication(...) {//对从AMS中获取的信息进行封装...//发送MessagesendMessage(H.BIND_APPLICATION, data);}//Handler处理会调用到这里来private void handleBindApplication(AppBindData data) {try {//创建ApplicationApplication app = data.info.makeApplication(data.restrictedBackupMode, null);mInitialApplication = app;//cotentprovider初始化,即为什么能用它初始化业务代码installContentProviders(app, data.providers); try {//启动生命周期onCreate()mInstrumentation.callApplicationOnCreate(app);}}
    //LoadedApk中public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {//如果存在Application的实例,则直接返回,这也说明Application是个单例if (mApplication != null) {return mApplication;}Application app = null;//反射初始化Application...//交由 Instrumentation 创建并调用 Application 的 onCreate()if (instrumentation != null) {try {instrumentation.callApplicationOnCreate(app);}}return app;}

3.3.2 启动 Activity

attachApplicationLocked() 中会调用 stack.getAllRunningVisiableActivitiesLocked() 将栈中所有的 Activity 信息都赋值给一个列表 mTmpActivityList 然后遍历生成一个个配置类对象 ActivityRecord,通过 realStartActivityLocked() 传递,方法体中通过 ClientTransaction.obtain() 创建 Activity 启动事务并添加一个回调持有 LaunchActivityItem,事务被提交给 ClientLifecyleManager 最通过 ApplicationThread 回调到 ActivityThread 中处理。发送 Message 然后 Handler 处理(从Binder线程切换到主线程),会调用 TransactionExecutor 执行事务。 方法中会遍历所有添加的回调,拿到 LaunchActivityItem 父类 ClientTransctionItem 调用 execute(),创建一个配置类对象 ActivityRecord 赋值各种信息,回调给ActivityThread.handleLaunchActivity(),调用 performLaunchActivity(),进而通过 Instrumentation.newActivity() 创建 Activity(通过Intent得到类名用类加载器反射加载),通过 Instrumentation.callActivityOnCreate() 执行 onCreate() 生命周期。

    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {if (realStartActivityLocked(hr, app, true, true)) {...}          }final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException {try {调用ApplicationThread的scheduleLaunchActivity用于启动一个Activityapp.thread.scheduleLaunchActivity(...);}}
//ActivityThread中
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {//创建ActivityActivity a = performLaunchActivity(r, customIntent);//渲染Activityif (a != null) {handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);}
}

3.4 UI 渲染过程

3.4.1 设置布局

Activity 被启动后会执行 onCreate(),我们会在其中调用 setContentView() 来设置布局文件,该方法会调用 getWindow() 获取自己持有的 Window 对象(子类PhoneWindow,创建时在 performLaunchActivit() 中调用 activity.attach() 时被赋值的)并调用它的 setContentView(),初始化 DecorView 并将我们自定义的布局加载到 content 中。

//Activity中
public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();
}//PhoneWindow中
public void setContentView(int layoutResID) {if (mContentParent == null) {//初始化DecorViewinstallDecor();}
}
private void installDecor() {//初始化DecorView(加载系统的基础布局)mDecor = generateDecor(-1);//获取DecorView中Content部分,加载我们自定义的布局内容mContentParent = generateLayout(mDecor);
}

3.4.2 渲染布局

通过 addView() 将控件添加到 ViewGroup 中会触发 requestLayout() 和 invalidate(true)  重新布局和刷新,由于 DacorView 没有父容器所以只会执行添加操作。

//ViewGroup中
public void addView(View child, int index, LayoutParams params) {requestLayout();invalidate(true);
}//View中
public void requestLayout() {if (mParent != null && !mParent.isLayoutRequested()) {mParent.requestLayout();}
}

        对于 Activity 生命周期一直说 onStart() 是可见的,performStart() 中调用 Instrumentation.callActivityOnStart() 并没有做渲染处理,所以这个“可见”严谨来说是针对是否渲染。

        在 ActivityThread.handleResumeActivity() 中将 DecorView 添加到了 ViewManager 中,进而调用 WindowManagerGlobal.addView(),将 DecorView 设置给了在这里创建的 ViewRootImpl 对象,ViewRootImpl 会执行 requestLayout() → scheduleTraversals() → doTraversal() → performTraversals(),而这最后一个方法里会执行 View 的绘制流程(测量、摆放、绘制),此时 Activity 就被渲染出来了。

//ActivityThread中
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {//调用Activity.onResume()r = performResumeActivity(token, clearHide, reason);if (r != null) {final Activity a = r.activity;if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;...//间接调用到WindowManagerGlobal的addView方法wm.addView(decor, l);}}
}
//WindowManagerGlobal中
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {root = new ViewRootImpl(view.getContext(), display);//Decor设置给了ViewRootImplroot.setView(view, wparams, panelParentView);
}

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

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

相关文章

Linux---信号

前言 到饭点了,我点了一份外卖,然后又开了一把网游,这个时候,我在打游戏的过程中,我始终记得外卖小哥会随时给我打电话,通知我我去取外卖,这个时候游戏还没有结束。我在打游戏的过程中需要把外…

Cannot resolve plugin org.apache.maven.plugins:maven-compiler-plugin:3.8.1

目录 【问题描述】maven环境报错 Cannot resolve plugin org.apache.maven.plugins:maven-compiler-plugin:3.8.1 【解决办法】 检查maven路径是否一致 路径一致的话&#xff0c;更改配置文件settings.xml的镜像源。 添加代码到 <mirrors> <!-- 阿里镜像 --> &l…

网络原理-TCP/IP(5)

TCP协议 延迟应答 它也是基于滑动窗口,提高效率的一种机制,结合滑动窗口以及流量控制,能够以延迟应答ACK的方式,把反馈的窗口,搞大.核心在于允许范围内,让窗口尽可能大. 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小. 1.假设接收端缓冲区为1M.一次收到了5…

新手小白零基础一分钟自动部署搭建幻兽帕鲁服务器教程

对于初次接触游戏联机服务器搭建的新手玩家来说&#xff0c;部署和配置一个像“幻兽帕鲁”这样的网络游戏联机服务器可能会显得有些复杂。但别担心&#xff0c;本文将提供一种快速、简洁且适合零基础新手的一分钟自动部署搭建幻兽帕鲁游戏联机服务器的详细教程。 本文将为大家提…

Linux 驱动开发基础知识——内核对设备树的处理与使用(十)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

Oracle闪回日志管理(flashbackup log)

Oracle闪回日志管理&#xff08;flashbackup log&#xff09; 1.开启闪回日志 shutdown immediate startup mount; alter database archivelog; alter database flashback on; alter database open; 2、查看闪回日志保留期限 show parameter flash 默认是1440分钟&#xff0c…

【LeetCode】刷题总结 - 15. 三数之和

15. 三数之和 | LeetCode 思路 首先对 nums 进行排序&#xff0c;然后设置三个指针&#xff1a;left、mid、right&#xff1a; left 从最左边开始&#xff0c;依次向后遍历每次固定住 left 后&#xff0c;就化为一个 2sum 问题&#xff1a; mid left 1&#xff0c;right …

python中[[]] * (n)和[[] for _ in range(n)]的区别

1、现象 刷leetcode207的时候碰到一个坑&#xff0c;用[[]] * (n)初始化二维数组&#xff0c;逻辑是正确的&#xff0c;但是结果始终不对。 2、原因 最后定位是初始化语句使用错误导致的&#xff0c;我使用的是[[]] * (n)&#xff0c;应该使用[[] for _ in range(n)] 3、解…

SpringBoot:实例一

一、实现的效果 在浏览器地址栏输入http://localhost:8080/hello&#xff0c;当前页面显示hello world 实例一代码&#xff1a;点击查看LearnSpringBoot01 点击查看更多的SpringBoot教程 二、 效果图 三、 pom.xml代码 <?xml version"1.0" encoding"UTF-…

【快速上手QT】01-QWidgetQMainWindow QT中的窗口

总所周知&#xff0c;QT是一个跨平台的C图形用户界面应用程序开发框架。它既可以开发GUI程序&#xff0c;也可用于开发非GUI程序&#xff0c;当然我们用到QT就是要做GUI的&#xff0c;所以我们快速上手QT的第一篇博文就讲QT的界面窗口。 我用的IDE是VS2019&#xff0c;使用QTc…

【小白学unity记录】使用unity播放声音

1. 示例 unity中播放声音涉及到两个组件。AudioSource和AudioClip。AudioSource可以理解为播放器&#xff0c;AudioClip可以理解为音频片段文件。AudioSource可以通过.clip属性切换音频片段。 using UnityEngine;public class PlayerController : MonoBehaviour {private int…

Windows11通过Hyper-V创建VM,然后通过vscode连接vm进行开发

这边需要在win11上建立vm来部署docker(这边不能用windows版本的docker destop)&#xff0c;学习了下&#xff0c;记录。 下载系统镜像 首先下载系统镜像&#xff1a;https://releases.ubuntu.com/focal/ 这边使用的是ubuntu20.04.6 LTS (Focal Fossa) &#xff0c;Server inst…

Facebook的数字合作愿景:创新与未来发展

随着科技的飞速发展&#xff0c;Facebook一直处于数字创新的前沿&#xff0c;致力于构建开放、智能、社交的数字社交体验。本文将深入探讨Facebook的数字合作愿景&#xff0c;探索其在创新与未来发展方面的雄心壮志。 引言 在当今数字化时代&#xff0c;社交媒体不仅是人们沟通…

Vue3+vite搭建基础架构(5)--- 使用vue-i18n

Vue3vite搭建基础架构&#xff08;5&#xff09;--- 使用vue-i18n 说明官方文档安装vue-i18n使用vue-i18n测试vue-i18n的国际化配置 说明 这里记录下自己在Vue3vite的项目使用vue-i18n做国际化语言的过程&#xff0c;不使用ts语法&#xff0c;方便以后直接使用。这里承接自己的…

uniapp中配置开发环境和生产环境

uniapp在开发的时候&#xff0c;可以配置多种环境&#xff0c;用于自动切换IP地址&#xff0c;用HBuilder X直接运行的就是开发环境&#xff0c;用HBuilder X发布出来的&#xff0c;就是生产环境。 1.使用HBuilder X创建原生的uniapp程序 选择vue3 2.什么都不改&#xff0c;就…

如何开通GitHub Copilot

GitHub Copilot 是由GitHub 和OpenAI共同开发的人工智能代码辅助工具&#xff0c;可以自动地生成高质量代码片段、上下文信息等。 通过自然语言处理和机器学习技术&#xff0c;能够通过分析程序员编写的代码、注释和上下文信息&#xff0c;自动生成代码&#xff0c;减轻程序员的…

【Springcloud篇】学习笔记十(十七章):Sentinel实现熔断与限流——Hystrix升级

第十七章_Sentinel实现熔断与限流 1.Sentinel介绍 1.1是什么 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 用来代替Hystrix Sentinel 具有…

NFTScan 与 Merlin Protocol 共同推出 BRC20 Indexer Oracle,于今日正式上线!

近日&#xff0c;NFT 数据基础设施 NFTScan 与 Merlin Protocol 进行战略合作&#xff0c;联合推出了比特币网络原生资产 Indexer Oracle 服务&#xff0c;现在该服务已在 NFTScan 开发者平台上线&#xff0c;任何开发者都可以注册使用&#xff01; Merlin Protocol 是一个专用…

Swift Vapor 教程(查询数据、插入数据)

上一篇简单写了 怎么创建 Swift Vapor 项目以及在开发过程中使用到的软件。 这一篇写一个怎么在创建的项目中创建一个简单的查询数据和插入数据。 注&#xff1a;数据库配置比较重要 先将本地的Docker启动起来&#xff0c;用Docker管理数据库 将项目自己创建的Todo相关的都删掉…

算法笔记刷题日记——3.简单入门模拟 3.1简单模拟

刷题日记 3.1 简单模拟 此类题型根据题目描述进行代码的编写&#xff0c;考察代码能力&#xff0c;刷题记录如下&#xff1a; B1001 B1032 B1016 B1026 B1046 B1008 B1012 B1018 A1042 A1046 A1065 B1010 A1002 A1009 错题记录 B1008 数组元素循环右移问题 一个数组_A_中存有…