一篇文章搞定《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的启动是第一扇门。