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();
    //实例化ActivityThread
    ActivityThread 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 {
                //传入用来通信的桥梁 ApplicationThread
                mgr.attachApplication(mAppThread);
            } ...
        }
    }
    //AMS中
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            attachApplicationLocked(thread, callingPid);
        }
    }

    private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
        //启动Application
		thread.bindApplication(...);
		//启动Activity
        if (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中获取的信息进行封装
        ...
        //发送Message
        sendMessage(H.BIND_APPLICATION, data);
    }

    //Handler处理会调用到这里来
    private void handleBindApplication(AppBindData data) {
        try {
            //创建Application
            Application 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用于启动一个Activity
            app.thread.scheduleLaunchActivity(...);
        }
    }
//ActivityThread中
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    //创建Activity
    Activity a = performLaunchActivity(r, customIntent);
    //渲染Activity
    if (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) {
        //初始化DecorView
        installDecor();
    }
}
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设置给了ViewRootImpl
    root.setView(view, wparams, panelParentView);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解析Excel文件内容,按每列首行元素名打印出某个字符串的统计占比(超详细)

目录 1.示例: 1.1 实现代码1:列数为常量 运行结果: 1.2 实现代码2:列数为变量 运行结果: 1.示例: 开发需求:读取Excel文件,统计第3列到第5列中每列的"False"字段占…

STM32SPI通信协议--(2)W25Q64简介

一、W25Q64简介 1、W25Qxx中的xx是不同的数字,表示了这个芯片不同的存储容量; 2、存储器分为易失性与非易失性,主要区别是存储的数据是否是掉电不丢失: 易失性存储器:SRAM、DRAM; 非易失性存储器&#xff…

django+flask+python高校教材管理系统47nia

本.4论文结构 绪论:剖析项目可行性,表明研究方向。 开发技术:系统关键运用了Python技术性、Django框架、B/S架构和myspl数据库查询,并进行了详细介绍[6]。 系统分析:包含系统的总体构造,用例图和结构图。 系…

使用机器学习算法预测在线订餐需求

咱们国内的美团和国外的 Swiggy 和 Zomato 引入市场后,在线订餐的需求量很大。食品配送公司利用客户的购买习惯来加快配送过程。食品订单预测系统是这些公司可以用来加快整个交付过程的有用技术之一。 这些公司对客户的主要目标是在正确的时间交付食物。为了更快地…

二叉树的层序遍历 II

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[[15,7],[9,20],…

【数据开发】pyspark入门与RDD编程

【数据开发】pyspark入门与RDD编程 文章目录 1、pyspark介绍2、RDD与基础概念3、RDD编程3.1 Transformation/Action3.2 数据开发流程与环节 1、pyspark介绍 pyspark的用途 机器学习专有的数据分析。数据科学使用Python和支持性库的大数据。 spark与pyspark的关系 spark是一…

[BUUCTF]-PWN:roarctf_2019_easy_pwn解析

先看保护 64位,got表不可写 看ida 大致就是alloc创建堆块,fill填充堆块,free释放堆块,show输出堆块内容 这里要注意的点有以下 alloc创建堆块:这里采用的是calloc而不是malloc,calloc在创建堆块时会初始…

小白水平理解面试经典题目_二维数组类LeetCode 2966 Divide Array【排序算法实现】

2966 将数组划分为具有最大差值的数组 小白渣翻译: 给定一个大小为 n 的整数数组 nums 和一个正整数 k 。 将数组分成一个或多个大小为 3 的数组,满足以下条件: nums 的每个元素都应该位于一个数组中。一个数组中任意两个元素之间的差异小…

python打造光斑处理系统6:高斯拟合

文章目录 构建拟合函数数据获取打印信息 光斑处理:python处理高斯光束的图像 光斑处理系统: 程序框架🌟打开图像🌟参数对话框/伪彩映射🌟裁切ROI光强分布 构建拟合函数 scipy中提供了非线性最小二乘回归算法&#x…

创建型模式-单例模式:定义、实现及应用

目录 一、模式定义二、针对问题1.解决的问题2.解决方案3.举个例子4.设计模式适合场景5.实现方式6.优缺点7.与其他模式的关系 三、代码实现 一、模式定义 单例模式(Singleton Pattern)是一种创建型模式,用于限制某个类只能创建一个对象。它提…

CMake Msys2 搭配vscode

(一)MSYS2介绍 MSYS2(Minimal SYStem 2)是一个集成了大量的GNU工具链、工具和库的开源软件包集合。它提供了一个类似于Linux的shell环境,可以在Windows系统中编译和运行许多Linux应用程序和工具。 MSYS2基于MinGW-w64平台,提供了…

04、全文检索 -- Solr -- 管理 Solr 的 core(使用命令和图形界面创建、删除 core,以及对core 目录下的各文件进行详细介绍)

目录 管理 Solr 的 core创建 Core方式1:solr 命令创建演示:使用 solr 命令创建 Core:演示:命令删除 Core(彻底删除) 方式2:图形界面创建Web控制台创建CoreWeb控制台删除 Core(未彻底…

使用css绘制小三角形

要使用CSS绘制小三角形&#xff0c;您可以使用border属性来设置边框样式。下面是一种常见的绘制小三角形的方法&#xff1a; <style>.box {width: 0;height: 0;/* border-top: 10px solid red; */border-bottom: 10px solid blue;border-left: 10px solid transparent;b…

【Mysql】事务的隔离级别与 MVCC

事务隔离级别 我们知道 MySQL 是一个 C/S 架构的服务&#xff0c;对于同一个服务器来说&#xff0c;可以有多个客户端与之连接&#xff0c;每个客户端与服务器连接上之后&#xff0c;就是一个会话&#xff08; Session &#xff09;。每个客户端都可以在自己的会话中向服务器发…

【算法与数据结构】718、1143、LeetCode最长重复子数组 最长公共子序列

文章目录 一、718、最长重复子数组二、1143、最长公共子序列三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、718、最长重复子数组 思路分析&#xff1a; 第一步&#xff0c;动态数组的含义。 d p [ i ] [ j ] dp[i]…

计算机视觉-PCV包、Vlfeat库、Graphviz库的下载安装配置及问题解决(使用anaconda3 python 3.8.5)

目录 一、PCV包配置 二、Vlfeat配置 三、在PCV包的sift.py文件中对路径进行修改 四、以上步骤所需注意的错误 五、Graphviz配置 一、PCV包配置 1.下载PCV包,点开网址直接下载安装包(不用解压),下载之后将安装包放在任意目录位置https://codeload.github.com/Li-Shu14…

Java_简单实现无头单向非循环链表_简单实现LinkedList

文章目录 一、ArrayList的优缺点二、链表1.链表的概念及结构2.链表的分类1、单向或者双向2、带头或者不带头3、循环或者非循环 三、实现无头单向非循环链表1.定义接口2.定义MySingleList3.成员1、节点类&#xff08;定义在MySingList类里&#xff09;2、头节点引用 4.打印链表实…

【服务器搭建】快速完成幻兽帕鲁服务器的搭建及部署【零基础上手】

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 教程详戳&#xff1a;不需要懂技术&#xff0c;1分钟幻兽帕鲁服…

Datax问题记录

1、同步mysql&#xff1a;OS errno 24 - Too many open files 2023-11-20 12:30:04.371 [job-0] ERROR JobContainer - Exception when job run com.alibaba.datax.common.exception.DataXException: Code:[DBUtilErrorCode-07], Description:[读取数据库数据失败. 请检查您的…

【Kafka】 幂等和事务详解

目录 幂等性为什么需要幂等性如何实现幂等性使用幂等幂等性的限制条件幂等性的实现原理 事务为什么需要事务开启事务事务保证事务恢复的保证事务原子性的保证事务中 Offset 的提交保证用于事务特性的控制型消息 事务流程事务原理FindCoordinatorRequestInitProducerIdRequest开…