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

2024-02-03 09:04

本文主要是介绍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);
}

这篇关于Framework - ActivityThread 应用启动UI渲染流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/673629

相关文章

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

VS配置好Qt环境之后但无法打开ui界面的问题解决

《VS配置好Qt环境之后但无法打开ui界面的问题解决》本文主要介绍了VS配置好Qt环境之后但无法打开ui界面的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目UKeLvb录找到Qt安装目录中designer.UKeLvBexe的路径找到vs中的解决方案资源

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

MySQL启动报错:InnoDB表空间丢失问题及解决方法

《MySQL启动报错:InnoDB表空间丢失问题及解决方法》在启动MySQL时,遇到了InnoDB:Tablespace5975wasnotfound,该错误表明MySQL在启动过程中无法找到指定的s... 目录mysql 启动报错:InnoDB 表空间丢失问题及解决方法错误分析解决方案1. 启用 inno

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

Python Flask 库及应用场景

《PythonFlask库及应用场景》Flask是Python生态中​轻量级且高度灵活的Web开发框架,基于WerkzeugWSGI工具库和Jinja2模板引擎构建,下面给大家介绍PythonFl... 目录一、Flask 库简介二、核心组件与架构三、常用函数与核心操作 ​1. 基础应用搭建​2. 路由与参

使用JavaConfig配置Spring的流程步骤

《使用JavaConfig配置Spring的流程步骤》JavaConfig是Spring框架提供的一种基于Java的配置方式,它通过使用@Configuration注解标记的类来替代传统的XML配置文... 目录一、什么是 JavaConfig?1. 核心注解2. 与 XML 配置的对比二、JavaConf

Spring Boot中的YML配置列表及应用小结

《SpringBoot中的YML配置列表及应用小结》在SpringBoot中使用YAML进行列表的配置不仅简洁明了,还能提高代码的可读性和可维护性,:本文主要介绍SpringBoot中的YML配... 目录YAML列表的基础语法在Spring Boot中的应用从YAML读取列表列表中的复杂对象其他注意事项总

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案

《无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案》:本文主要介绍了无法启动此程序,详细内容请阅读本文,希望能对你有所帮助... 在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是"api-ms-win-core-path-l1-1-0.dll丢失