一篇文章搞定《APP的启动流程》

news2024/12/27 12:41:56

一篇文章搞定《APP的启动流程》

  • 前言
  • 冷启动、温启动、热启动
  • 启动中的重要成员简介
    • zygote进程
    • Instrumentation
    • SystemServer进程
    • ActivityManagerService
    • Binder
    • ActivityThread
  • 启动的步骤详解
    • 一、点击桌面图标
    • 二、创建进程
    • 三、初始化APP进程
    • 四、APP进程与System_server的绑定
    • 五、初始化Applacation And Activity
  • 启动优化(浅谈)
    • 透明主题优化
    • 设置闪屏图片主题
    • Application 优化
    • 闪屏页业务优化
    • 利用Hook
  • 总结

前言

前面已经铺垫了Binder、Handler、View的绘制流程
那么该来看看APP的启动流程了,是如何启动了我们这些重要的组件
本文会按照步骤和启动需要的成员并附带一点点源码进行讲解。
以了解熟悉启动的流程为主。不会大篇幅的利用源码深入。
本文结构:
1、冷启动、温启动、热启动
2、启动中的重要成员简介
3、启动的步骤详解
4、启动优化(浅谈)

冷启动、温启动、热启动

  • 冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,然后再根据启动的参数,启动对应的进程组件,这个启动方式就是冷启动。
  • 温启动:当启动应用时,后台已有该应用的进程,但是Activity可能因为内存不足被回收。这样系统会从已有的进程中来启动这个Activity,这个启动方式叫温启动。
  • 热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动对应的进程组件,这个方式叫热启动。

由于冷启动相对于其他启动方式多了进程的创建(Zygote进程fork创建进程)以及应用的资源加载和初始化(Application的创建及初始化),所以相对来说会比较耗时,所以我们一般说的App启动优化一般指的都是App的冷启动优化。
启动中的重要

启动中的重要成员简介

zygote进程

不说的冠冕堂皇的,就简单的给大家总结两条:

  • zygote进程是由Linux中的init进程,fock出来的进程。
  • 在Android中,所有的应用的进程都是由zygote进程fork出来的,一个新的App进程就是zygote进程的子进程。

Zygote进程首先会fork自己孵化出的SystemServer进程,它的main函数主要负责:

  • 启动binder线程池,这是SystemServer与其他进程通信的基础
  • 初始化Looper
  • 创建了SystemServiceManager对象,它会启动Android中的各种服务。包括AMS、PMS、WMS
  • 启动桌面进程,这样才能让用户见到手机的界面。
  • 开启loop循环,开启消息循环,SystemServer进程一直运行,保障其他应用程序的正常运行。

注意:init进程在开启的时候就创建了,所以在开机的时候我们的zygote进程和ServiceManager都被创建出来了。

Instrumentation

工具类,它用来监控应用程序和系统的交互,包装了 ActivityManagerService 的调用,一些插件化方案就是通过 hook 该类实现的。

SystemServer进程

SystemServer是由zygote进程fork出来的第一个进程,SystemServer和Zygote是Android Framework最重要的2个进程。 系统里面重要的服务都是在这个进程里面开启的,比如ActivityManagerService、PackageManagerService、WindowManagerService。
应用启动流程基本是围绕着ActivityManagerService和ActivityThread展开。

ActivityManagerService

  • 在Android系统中,任何一个Activity的启动都是由AMS和App进程(主要是ActivityThread)相互配合来完成的。
  • 他在SystemServer创建后被初始化
  • App进程与AMS通过Binder机制进行跨进程通信
  • AMS(SystemServer进程)与zygote通过Socket进行跨进程通信。

Binder

Binder就不细说了,Android系统中的IPC跨进程通信。
就是《一篇文章搞定〈Binder〉》中的内容

ActivityThread

ActivityThread 是 Android 系统中驱动应用程序的主线程,它的作用是管理应用程序的生命周期和交互。ActivityThread 负责启动应用程序的入口 Activity,提供与 Android 系统之间的通信桥梁,同时也处理了应用程序的消息队列和事件循环。
在 App 启动过程中,ActivityThread 主要负责以下几个重要的工作:

  • 创建一个主线程 Looper ,用于处理消息队列和事件循环。
  • 加载应用程序的主题、资源和布局文件。
  • 通过调用 Instrumentation 的 callApplicationOnCreate() 方法触发应用程序的生命周期,初始化应用程序,并创建首个 Activity。

启动的步骤详解

图中的红色线条为Binder通信
紫色线条是Socket通信

一、点击桌面图标

  • Launcher 捕获点击事件,调用 Activity#startActivity();
  • 点击图标发生在Launcher应用的进程,startActivity()函数最终是由Instrumentation通过Android的Binder跨进程通信机制 发送消息给 system_server 进程;
    在这里插入图片描述

二、创建进程

在 system_server 中,启动进程的操作会先调用ActivityManagerService#startProcessLocked() 方法,该方法内部调用 Process.start(android.app.ActivityThread);而后通过 socket 通信告知 Zygote 进程 fork 子进程,即 app 进程。
在这里插入图片描述

三、初始化APP进程

  • 开启主线程 app 进程启动后,首先是实例化 ActivityThread,并执行其main()函数
  • main()函数中创建 ApplicationThread,Looper,Handler 对象,并开启主线程消息循环Looper.loop()。
  • 调用 ActivityThread#attach(false)方法进行 Binder 通信

源码如下:ActivityThread.java

public static void main(String[] args) {
···
     Looper.prepareMainLooper();

     ActivityThread thread = new ActivityThread();
     thread.attach(false);

     if (sMainThreadHandler == null) {
         sMainThreadHandler = thread.getHandler();
     }
 ···
     Looper.loop();
 ···
 }


 private void attach(boolean system) {
 ···
     if (!system) {
     ···
         final IActivityManager mgr = ActivityManager.getService();
         try {
             mgr.attachApplication(mAppThread);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
     ···
     } else {
    ···
     }
 ···
 }

在这里插入图片描述

四、APP进程与System_server的绑定

  • 调用 ActivityThread#attach(false)方法进行 Binder 通信(在方法里进行下一步)
  • 通知system_server进程执行 ActivityManagerService#attachApplication(mAppThread)方法
  • system_server进程在收到请求后,进行一系列准备工作后(创建该App进程信息表)再通过binder IPC向App进程发送scheduleLaunchActivity请求;
  • 这相当于是APP进程发信息给system_server进程,进行一个绑定的过程。创建通信的过程。
    在这里插入图片描述

五、初始化Applacation And Activity

  • App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程(ActivityThread)发送LAUNCH_ACTIVITY消息;
  • 主线程(ActivityThread)在收到Message后,通过HandleLaunchActivity创建目标Activity,并回调Activity.onCreate()等方法。
  • 到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面
    在这里插入图片描述
    到此为止App的启动流程就结束了,上图也是完整的启动流程模型图。

启动优化(浅谈)

透明主题优化

为了解决启动窗口白屏问题,使用透明主题来解决这个问题,但是治标不治本。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowIsTranslucent">true</item>
</style>

设置闪屏图片主题

这个挺多APP还在用的哦
为了更顺滑无缝衔接我们的闪屏页,可以在启动 Activity 的 Theme中设置闪屏页图片,这样启动窗口的图片就会是闪屏页图片,而不是白屏。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowBackground">@mipmap/launch_image</item>   //闪屏页图片  
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>
</style>

这样设置的话,就会在冷启动的时候,展示闪屏页的图片,等App进程初始化加载入口 Activity (也是闪屏页) 就可以无缝衔接。
其实这种方式并没有真正的加速应用进程的启动速度,而只是通过用户视觉效果带来的优化体验。

Application 优化

通过上面的流程,我们可以知道。会先初始化我们的Application,所以在Application中初始化减少耗时操作能有效的帮我提升。
通常,有机会优化这些工作以实现性能改进,这些常见问题包括:

  • 复杂繁琐的布局初始化
  • 阻塞主线程 UI 绘制的操作,如 I/O 读写或者是网络访问.
  • Bitmap 大图片或者 VectorDrawable加载
  • 其它占用主线程的操作

有很多第三方组件(包括App应用本身)都在 Application 中抢占先机,完成初始化操作。
比如Bugly,x5内核初始化,SP的读写,友盟等组件。那就放到子线程中去初始化。

闪屏页业务优化

例如埋点,点击流,数据库初始化等这类必须在主线程初始化的动作。那么我们可以放在闪屏页。
一般:闪屏页政展示总时间 = 组件初始化时间 + 剩余展示时间。
也就是2000ms的总时间,组件初始化了800ms,那么就再展示1200ms即可。

通过上面的流程分析,我们可以知道 Application 初始化后会调用 attachBaseContext() 方法,再调用 Application 的 onCreate(),再到入口 Activity的创建和执行 onCreate() 方法。所以我们就可以在 Application 中记录启动时间。
Application.java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    SPUtil.putLong("application\_attach\_time",
        System.currentTimeMillis());//记录Application初始化时间  
}

有了启动时间,我们得知道入口的 Acitivty 显示给用户的时间(View绘制完毕)
那就是入口Activity的onWindowFocusChanged喽
入口Activity.java

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    long appAttachTime = SPUtil.getLong("application\_attach\_time");
    long diffTime = System.currentTimeMillis() - appAttachTime;
    //从application到入口Acitity的时间  
    
    //所以闪屏页展示的时间为 2000ms - diffTime.  
}

利用Hook

  • ActivityManagerService

在Android系统中,启动应用的过程通常是通过ActivityManagerService来进行管理的。可以通过Hook ActivityManagerService的方式,在应用启动时进行一些优化操作。例如,可以在应用启动前创建一个空的Activity,并在其onCreate方法中执行一些耗时操作,如初始化数据或预加载资源。然后,通过Hook ActivityManagerService,将启动的Activity替换为这个空的Activity形式,这样就可以减少应用启动的耗时。

  • AMS和PMS

Android系统在应用启动的过程中,会对Activity、Service等组件的启动进行权限验证和安全检查。这个过程较为耗时,可以通过Hook ActivityManagerService(AMS)和PackageManagerService(PMS)来绕过这些检查,从而提升启动速度。例如,可以通过Hook AMS和PMS,修改应用的启动流程,跳过权限验证和安全检查的过程,从而减少启动耗时。

总结

APP的启动流程学问是很大的。需要对Android的源码进行一定的理解。
但是我们在开发中需要有意识的注意会影响APP启动的操作。
毕竟APP的启动是第一扇门。

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

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

相关文章

《深度解析Docker与微服务架构:构建灵活可扩展的现代应用》

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【SQL语句复习】第1-2章

SQL复习 学习目标&#xff1a;复习SQL语句 学习地址&#xff1a;https://linklearner.com/learn/detail/70 第一章 初始数据库 数据库是将大量数据保存起来&#xff0c;通过计算机加工而成的可以进行高效访问的数据集合。该数据集合称为数据库&#xff08;Database&#xf…

大学生用一周时间给麦当劳做了个App(Vue版)

背景 有个大学生粉丝最近私信联系我&#xff0c;说基于我之前开源的多语言项目做了个仿麦当劳的项目&#xff0c;虽然只是个样子货&#xff0c;但是收获颇多&#xff0c;希望把自己写的代码开源出来供大家一起学习进度。这个小伙伴确实是非常积极上进&#xff0c;很多大学生&a…

C盘空间不足:解决办法完整教程

当C盘空间不足时&#xff0c;你可以尝试以下几种解决方案&#xff1a; 1. 清理临时文件&#xff1a;使用Windows自带的磁盘清理工具&#xff0c;可以删除临时文件、回收站中的文件和其他不必要的系统文件&#xff0c;释放一些空间&#xff0c;推荐使用工具分区助手。 2. 卸载不…

数据库版本管理工具Flyway入门实战

From version control to continuous delivery, Flyway helps individuals, teams, and enterprises build on application delivery processes to automate database development. 1.引言 在项目开发中&#xff0c;一直在探索如何进行数据库的版本管理。关注的公众号推送了…

踩坑 视觉SLAM 十四讲第二版 ch8 编译及运行问题

1.fmt相关 CMakeLists.txt中&#xff1a;在后面加上 fmt target_link_libraries(optical_flow ${OpenCV_LIBS} fmt ) target_link_libraries(direct_method ${OpenCV_LIBS} ${Pangolin_LIBRARIES} fmt )2.不存在用户定义的从 "std::_Bind<void (OpticalFlowTracker::…

动态内存管理函数的使用与优化技巧(内存函数、柔性数组)

目录 前言 一、动态内存函数 为什么存在动态内存分配 动态内存函数介绍 malloc和free calloc realloc 常见的错误 经典笔试题目 二、C/C程序的内存开辟 三、柔性数组 柔性数组的特点&#xff1a; 柔性数组的使用 柔性数组的优势 前言 动态内存管理函数是C语言中非常重要的一部…

Verilog 学习之路(三)——牛客刷题篇

1.输入序列连续的序列检测 题面 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJH9kHFH-1690301233803)(https://s2.loli.net/2023/07/26/HJPXR2mhbaVCG6d.png)]思路 对于序列检测题目&#xff0c;常规的解法有两种&#xff1a;状态机法和序列缓存…

需求管理全过程流程图及各阶段核心关注点详解

分析报告指出&#xff0c;多达76%的项目失败是因为差劲的需求管理&#xff0c;这个是项目失败的最主要原因&#xff0c;比落后的技术、进度失控或者混乱的变更管理还要关键。很多项目往往在开始的时候已经决定了失败&#xff0c;谜底就在谜面上&#xff0c;开始就注定的失败&am…

python-基本数据类型

hello&#xff0c;这里是Token_w的文章&#xff0c;主要讲解python的基础学习&#xff0c;希望对大家有所帮助 整理不易&#xff0c;感觉还不错的可以点赞收藏评论支持&#xff0c;感谢&#xff01; 有没有一个人&#xff0c;你想给TA传个小纸条&#xff1f;用恺撒密码一展身手…

【Docker】Docker Compose的配置与部署

文章目录 一、Docker Compose1. Docker Compose 的概述2. Docker Compose 三大的概念3. Docker Compose 环境安装 二、YAML 文件格式及编写注意事项1. YAML 文件格式2. YAML 格式的注意事项3. YAML 数据结构3.1 基本类型3.2 实例3.3 YAML 特殊类型文本块锚点与引用 三、Docker …

【【直流电机驱动PWN】】

直流电机驱动PWN 前面都是沙县小吃&#xff0c;这里才是满汉全席 直流电机是一种电能转化成机械能的装置 直流电机有两个电极 当电机正接 电机正转 当电机负接 电机倒转 电机还有步进电机 舵机 无刷电机 空心杯电机 因为电机是一个大功率器件并不太好直接接在IO端口上所以我…

2023 年牛客多校第一场题解(上)

A Almost Correct 题意&#xff1a;给定长度为 n n n 的 01 01 01 串 s s s&#xff0c;构造一个排序网络&#xff0c;使得能够将除 s s s 之外的任意 01 01 01 序列正确排序&#xff0c;且 s s s 无法被正确排序。 T T T 组测试&#xff0c; 1 ≤ T ≤ 1 0 4 1 \le T \…

ASEMI代理海矽美快恢复二极管SFP6002的特性和应用分析

编辑-Z 二极管SFP6002是一种常见的电子元件&#xff0c;也被称为快恢复二极管。它具有快速恢复时间和低反向恢复电流的特点&#xff0c;适用于高频电路和开关电源等应用。 SFP6002的主要特性包括&#xff1a; 1. 快速恢复时间&#xff1a;SFP6002具有快速的恢复时间&#xff…

Git Gui相关术语

文章目录 Git Gui主界面相关术语- Amend Last Commit&#xff08;修正最后一次提交&#xff09;- Rescan&#xff08;重新扫描&#xff09;- Sign Off&#xff08;签名&#xff09;- Stage Changed Git Gui Commit菜单相关术语- Stage to Commit&#xff08;暂存到提交&#xf…

Ceph组件

Ceph组件 无论是想向云平台提供Ceph 对象存和 Ceph 块设备服务、部署Ceph 文件系统,或者是将 Ceph 用于其他目的,所有 Ceph 存储集群部署都从设置每个 Ceph 节点、网络开始。 一个Ceph 存储集群至少需要一个Ceph Monitor、Ceph Manager和 Ceph OSD (OBJECT STORAGE DAEMON对象存…

【MySQL】十三,索引的代价、MySQL数据结构选择的合理性

索引的代价 空间上的代价 每建立一个索引都要为它建立一棵B树&#xff0c;每一棵B树的每一个节点都是一个数据页&#xff0c;一个页默认会占用 16KB 的存储空间&#xff0c;一棵很大的B树由许多数据页组成&#xff0c;那就是很大的一片存储空间。 时间上的代价 每次对表中的…

前端开发中的常见优化

目录 外观 兼容 不同尺寸&#xff08;包裹&#xff0c;height:100%&#xff09; 不同 浏览器 隐藏滚动条 的 不同属性名 重排->重绘 不显示 display:none->禁用disable 性能 导航重复&#xff08;修改原型push、replace方法&#xff09; 搜索防抖 import { debo…

【Docker】Docker私有仓库管理

目录 一 、Harbor 简介1.1 什么是Harbor1.2Harbor的特性1.3Harbor的构成 二、Harbor部署2.1 部署 Docker-Compose 服务2.2 部署 Harbor 服务2.3启动Harbor2.4 创建一个新项目2.5 在其他客户端上传镜像 三、配置Harbor 高可用四、维护管理Harbor4.1. 通过 Harbor Web 创建项目4.…

提升Web3安全性和用户体验:元事务和加密技术的应用

在Web3中&#xff0c;去中心化应用程序&#xff08;DApps&#xff09;是一种基于区块链技术的应用程序&#xff0c;它们通过智能合约实现透明、安全、去中心化的业务逻辑。然而&#xff0c;DApps的使用门槛比传统的中心化应用程序更高&#xff0c;需要用户具备一定的技术知识&a…