一个APP从启动到主页面显示经历了哪些过程?(App启动流程,从点击桌面开始)

本文以图文形式概述了APP从启动到主页面显示的过程,包括Launcher启动请求、Zygote进程创建、SystemServer进程协调及Binder通信机制等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文以图文并茂的形式简单介绍一个APP从启动到主页面显示经历了哪些流程,以及实现的原理。不介绍具体源码,仅仅构建一个大体框架。

原地址链接:http://www.jianshu.com/p/a72c5ccbd150

一、流程概述

这里写图片描述

启动流程:

①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;

②system_server进程接收到请求后,向zygote进程发送创建进程的请求;

③Zygote进程fork出新的子进程,即App进程;

④App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;

⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;

⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;

⑦主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。

⑧到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。

上面的一些列步骤简单介绍了一个APP启动到主页面显示的过程,可能这些流程中的一些术语看的有些懵,什么是Launcher,什么是zygote,什么是applicationThread…..

下面我们一一介绍。

二、理论基础

1.zygote

zygote意为“受精卵“。Android是基于Linux系统的,而在Linux中,所有的进程都是由init进程直接或者是间接fork出来的,zygote进程也不例外。

在Android系统里面,zygote是一个进程的名字。Android是基于Linux System的,当你的手机开机的时候,Linux的内核加载完成之后就会启动一个叫“init“的进程。在Linux System里面,所有的进程都是由init进程fork出来的,我们的zygote进程也不例外。

我们都知道,每一个App其实都是

● 一个单独的dalvik虚拟机
● 一个单独的进程

所以当系统里面的第一个zygote进程运行之后,在这之后再开启App,就相当于开启一个新的进程。而为了实现资源共用和更快的启动速度,Android系统开启新进程的方式,是通过fork第一个zygote进程实现的。所以说,除了第一个zygote进程,其他应用所在的进程都是zygote的子进程,这下你明白为什么这个进程叫“受精卵”了吧?因为就像是一个受精卵一样,它能快速的分裂,并且产生遗传物质一样的细胞!

2.system_server

SystemServer也是一个进程,而且是由zygote进程fork出来的。

知道了SystemServer的本质,我们对它就不算太陌生了,这个进程是Android Framework里面两大非常重要的进程之一——另外一个进程就是上面的zygote进程。

为什么说SystemServer非常重要呢?因为系统里面重要的服务都是在这个进程里面开启的,比如
ActivityManagerService、PackageManagerService、WindowManagerService等等。

3.ActivityManagerService

ActivityManagerService,简称AMS,服务端对象,负责系统中所有Activity的生命周期。

ActivityManagerService进行初始化的时机很明确,就是在SystemServer进程开启的时候,就会初始化ActivityManagerService。

下面介绍下Android系统里面的服务器和客户端的概念。

其实服务器客户端的概念不仅仅存在于Web开发中,在Android的框架设计中,使用的也是这一种模式。服务器端指的就是所有App共用的系统服务,比如我们这里提到的ActivityManagerService,和前面提到的PackageManagerService、WindowManagerService等等,这些基础的系统服务是被所有的App公用的,当某个App想实现某个操作的时候,要告诉这些系统服务,比如你想打开一个App,那么我们知道了包名和MainActivity类名之后就可以打开

Intent intent = new Intent(Intent.ACTION_MAIN);  
intent.addCategory(Intent.CATEGORY_LAUNCHER);              
ComponentName cn = new ComponentName(packageName, className);              
intent.setComponent(cn);  
startActivity(intent);

但是,我们的App通过调用startActivity()并不能直接打开另外一个App,这个方法会通过一系列的调用,最后还是告诉AMS说:“我要打开这个App,我知道他的住址和名字,你帮我打开吧!”所以是AMS来通知zygote进程来fork一个新进程,来开启我们的目标App的。这就像是浏览器想要打开一个超链接一样,浏览器把网页地址发送给服务器,然后还是服务器把需要的资源文件发送给客户端的。

知道了Android Framework的客户端服务器架构之后,我们还需要了解一件事情,那就是我们的App和AMS(SystemServer进程)还有zygote进程分属于三个独立的进程,他们之间如何通信呢?

App与AMS通过Binder进行IPC通信,AMS(SystemServer进程)与zygote通过Socket进行IPC通信。后面具体介绍。

那么AMS有什么用呢?在前面我们知道了,如果想打开一个App的话,需要AMS去通知zygote进程,除此之外,其实所有的Activity的开启、暂停、关闭都需要AMS来控制,所以我们说,AMS负责系统中所有Activity的生命周期。

在Android系统中,任何一个Activity的启动都是由AMS和应用程序进程(主要是ActivityThread)相互配合来完成的。AMS服务统一调度系统中所有进程的Activity启动,而每个Activity的启动过程则由其所属的进程具体来完成。

4.Launcher

当我们点击手机桌面上的图标的时候,App就由Launcher开始启动了。但是,你有没有思考过Launcher到底是一个什么东西?

Launcher本质上也是一个应用程序,和我们的App一样,也是继承自Activity

packages/apps/Launcher2/src/com/android/launcher2/Launcher.java

public final class Launcher extends Activity
        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
                   View.OnTouchListener {
                   }

Launcher实现了点击、长按等回调接口,来接收用户的输入。既然是普通的App,那么我们的开发经验在这里就仍然适用,比如,我们点击图标的时候,是怎么开启的应用呢?捕捉图标点击事件,然后startActivity()发送对应的Intent请求呗!是的,Launcher也是这么做的,就是这么easy!

5.Instrumentation和ActivityThread

每个Activity都持有Instrumentation对象的一个引用,但是整个进程只会存在一个Instrumentation对象。
Instrumentation这个类里面的方法大多数和Application和Activity有关,这个类就是完成对Application和Activity初始化和生命周期的工具类。Instrumentation这个类很重要,对Activity生命周期方法的调用根本就离不开他,他可以说是一个大管家。

ActivityThread,依赖于UI线程。App和AMS是通过Binder传递信息的,那么ActivityThread就是专门与AMS的外交工作的。

6.ApplicationThread

前面我们已经知道了App的启动以及Activity的显示都需要AMS的控制,那么我们便需要和服务端的沟通,而这个沟通是双向的。

客户端–>服务端
这里写图片描述
而且由于继承了同样的公共接口类,ActivityManagerProxy提供了与ActivityManagerService一样的函数原型,使用户感觉不出Server是运行在本地还是远端,从而可以更加方便的调用这些重要的系统服务。

服务端–>客户端

还是通过Binder通信,不过是换了另外一对,换成了ApplicationThread和ApplicationThreadProxy。
这里写图片描述
他们也都实现了相同的接口IApplicationThread

private class ApplicationThread extends ApplicationThreadNative {}

  public abstract class ApplicationThreadNative extends Binder implements IApplicationThread{}

  class ApplicationThreadProxy implements IApplicationThread {}

关于Binder通信,可以参考这两篇文章理解一下:简单理解Binder机制的原理,关于AIDL使用和Binder机制详解,你只需要看这一篇即可。

好了,前面罗里吧嗦的一大堆,介绍了一堆名词,可能不太清楚,没关系,下面结合流程图介绍。

三、启动流程

1.创建进程

①先从Launcher的startActivity()方法,通过Binder通信,调用ActivityManagerService的startActivity方法。

②一系列折腾,最后调用startProcessLocked()方法来创建新的进程。

③该方法会通过前面讲到的socket通道传递参数给Zygote进程。Zygote孵化自身。调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。

④调用ActivityThread.main()方法,ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环。

方法调用流程图如下:

这里写图片描述

更直白的流程解释:

这里写图片描述

①App发起进程:当从桌面启动应用,则发起进程便是Launcher所在进程;当从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送消息给system_server进程;

②system_server进程:调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求;

③zygote进程:在执行ZygoteInit.main()后便进入runSelectLoop()循环体内,当有客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork出新的应用进程;

④新进程:执行handleChildProc方法,最后调用ActivityThread.main()方法。

2.绑定Application

上面创建进程后,执行ActivityThread.main()方法,随后调用attach()方法。

将进程和指定的Application绑定起来。这个是通过上节的ActivityThread对象中调用bindApplication()方法完成的。该方法发送一个BIND_APPLICATION的消息到消息队列中, 最终通过handleBindApplication()方法处理该消息. 然后调用makeApplication()方法来加载App的classes到内存中。

方法调用流程图如下:

这里写图片描述

更直白的流程解释:

这里写图片描述

(如果看不懂AMS,ATP等名词,后面有解释)

3.显示Activity界面

经过前两个步骤之后, 系统已经拥有了该application的进程。 后面的调用顺序就是普通的从一个已经存在的进程中启动一个新进程的activity了。

实际调用方法是realStartActivity(), 它会调用application线程对象中的scheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列中, 通过 handleLaunchActivity()来处理该消息。在 handleLaunchActivity()通过performLaunchActiivty()方法回调Activity的onCreate()方法和onStart()方法,然后通过handleResumeActivity()方法,回调Activity的onResume()方法,最终显示Activity界面。

这里写图片描述

更直白的流程解释:
这里写图片描述

四、Binder通信

这里写图片描述

简称:

ATP: ApplicationThreadProxy

AT: ApplicationThread

AMP: ActivityManagerProxy

AMS: ActivityManagerService

图解:

①system_server进程中调用startProcessLocked方法,该方法最终通过socket方式,将需要创建新进程的消息告知Zygote进程,并阻塞等待Socket返回新创建进程的pid;

②Zygote进程接收到system_server发送过来的消息, 则通过fork的方法,将zygote自身进程复制生成新的进程,并将ActivityThread相关的资源加载到新进程app process,这个进程可能是用于承载activity等组件;

③ 在新进程app process向servicemanager查询system_server进程中binder服务端AMS, 获取相对应的Client端,也就是AMP. 有了这一对binder c/s对, 那么app process便可以通过binder向跨进程system_server发送请求,即attachApplication()

④system_server进程接收到相应binder操作后,经过多次调用,利用ATP向app process发送binder请求, 即bindApplication.
system_server拥有ATP/AMS, 每一个新创建的进程都会有一个相应的AT/AMP,从而可以跨进程 进行相互通信. 这便是进程创建过程的完整生态链。

以上大概介绍了一个APP从启动到主页面显示经历的流程,主要从宏观角度介绍了其过程,具体可结合源码理解。

参考文章:

startActivity启动过程分析
理解Android进程创建流程
[译]Android Application启动流程分析
【凯子哥带你学Framework】Activity启动过程全解析

<think>我们首先需要解决两个问题:1. 将APP设置为桌面应用;2. 启用开机自启动功能。根据提供的引用信息,我们分别处理。 1. 设置桌面应用(Launcher): 在Android中,桌面应用是响应`ACTION_MAIN`和`CATEGORY_HOME`意图的应用。因此,我们需要在AndroidManifest.xml中为我们的Activity设置相应的intent-filter。 2. 开机自启动: 根据引用[2][3][4],我们知道可以通过监听`BOOT_COMPLETED`广播来实现。但是,从Android 11开始,系统对自启动行为进行了限制(引用[1]提到系统App以外的其他App默认不允许自启动)。因此,我们需要: a. 在AndroidManifest.xml中声明接收广播的权限和接收器。 b. 处理可能由于系统限制导致的自启动失败(可能需要特殊权限或者将应用设置为系统应用,但普通应用通常无法做到,所以可能需要用户手动授权)。 步骤: 一、设置桌面应用: 在需要作为桌面Activity的<intent-filter>中添加: <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> (可选,但推荐) 这样,当用户按下Home键时,系统会弹出选择框,让用户选择使用哪个应用作为桌面,选择我们的应用并设为默认即可。 二、开机自启动: 1. 创建一个广播接收器(BroadcastReceiver)来接收`BOOT_COMPLETED`广播。 2. 在AndroidManifest.xml中声明该接收器,并添加权限和intent-filter。 3. 注意:从Android 8.0(API 26)开始,对隐式广播做了限制,但`BOOT_COMPLETED`是例外,仍然可以静态注册。 4. 但是,从Android 10开始,后台启动Activity受到限制,因此我们需要考虑在接收广播后启动一个Service(或者使用前台服务)来启动我们的Activity。但是,直接启动Activity在后台是不允许的,所以可能需要使用全屏Intent(比如在锁屏上显示)或者启动一个前台服务然后由服务启动Activity(这可能会引起不好的用户体验,因为用户开机后突然弹出应用界面)。因此,更好的做法可能是开机后只是启动服务,然后在适当的时候(比如用户解锁后)再启动Activity。但作为桌面应用,我们可能希望开机后直接进入我们的桌面,那么我们需要在广播接收器中启动我们的主Activity桌面Activity),并且需要处理后台启动Activity的限制。 处理后台启动Activity的限制: 在Android 10及以上,如果应用在后台,则不允许启动Activity,除非有特殊情况(如用户交互、通知点击等)。但是,开机自启动时,我们的应用并没有在后台运行,而是由系统启动我们的广播接收器,此时我们处于一个短暂的临时状态。实际上,开机完成后,系统会发送广播,此时我们的接收器被唤醒,然后启动Activity。根据Android官方文档,在接收广播后立即启动Activity是允许的,因为此时用户刚刚开机,还没有与任何应用交互,所以我们的启动会被视为用户开机流程的一部分。 但是,为了确保在Android 11上能够自启动,我们还需要注意: - 在AndroidManifest.xml中声明`RECEIVE_BOOT_COMPLETED`权限: <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> - 静态注册广播接收器,并添加intent-filter: <receiver android:name=".BootCompletedReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> 5. 另外,根据引用[1],系统源码中可能限制了非系统App的自启动行为。如果我们有系统源码,可以修改系统源码(如引用[1]和引用[3]提到的修改系统服务)来允许我们的App启动。但如果没有系统源码(即作为普通第三方应用),则只能依赖系统默认行为。在Android 11上,普通应用通过广播接收器接收`BOOT_COMPLETED`广播仍然是可行的,但需要用户手动授权(比如在系统设置中允许应用的自启动权限,不同厂商可能有不同的设置路径)。 因此,我们还需要在应用中引导用户去设置自启动权限(在设置->应用->自启动管理,或者电池优化设置中允许)。 三、代码实现: 1. 广播接收器示例(BootCompletedReceiver.java): ```java public class BootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { // 启动Activity桌面Activity) Intent mainIntent = new Intent(context, MainActivity.class); // 因为从非Activity上下文启动Activity需要添加FLAG_ACTIVITY_NEW_TASK mainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(mainIntent); } } } ``` 2. AndroidManifest.xml示例: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <!-- 声明接收开机广播的权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application ...> <!-- 桌面Activity --> <activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!-- 开机自启动广播接收器 --> <receiver android:name=".BootCompletedReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest> ``` 注意:在Android 11上,即使我们这样做了,系统可能仍然会阻止我们的应用自启动,因为系统对后台启动Activity有更严格的限制。因此,我们需要测试在Android 11上的行为。如果发现无法启动,可以尝试使用全屏Intent(使用`FLAG_ACTIVITY_NEW_TASK`和`FLAG_ACTIVITY_CLEAR_TASK`)或者使用一个透明的Activity作为跳板。 另外,从Android 10开始,系统对后台启动Activity的限制可能会导致我们的Activity无法启动。我们可以尝试在启动Activity时添加以下标志: mainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 但是,根据Android官方文档,在接收`BOOT_COMPLETED`广播后立即启动Activity是允许的,因为此时系统刚刚启动,用户还没有开始使用设备,所以我们的启动被视为用户启动流程的一部分。不过,不同厂商的定制系统可能会有不同的行为,因此需要针对不同设备进行适配。 四、用户手动设置: 由于不同厂商的Android系统(如华为、小米、OPPO等)对自启动管理有不同的策略,我们需要在应用内引导用户到系统设置中开启自启动权限。可以通过以下步骤: - 检测是否已开启自启动权限(通常无法直接检测,所以可以尝试在开机后检查是否执行了广播接收器,然后提示用户)。 - 如果发现没有执行,则弹窗提示用户,并跳转到自启动设置页面(不同厂商的跳转Intent可能不同,需要收集各厂商的跳转方式)。 总结: 实现桌面应用和开机自启动需要分别设置。桌面应用通过设置Activity的intent-filter实现。开机自启动通过接收`BOOT_COMPLETED`广播并启动Activity实现,同时需要注意系统限制和用户授权问题。 由于Android 11对后台启动Activity的限制,我们可能需要特殊处理。另外,不同厂商的定制系统可能需要用户手动开启自启动权限,因此应用内需要增加引导用户设置的逻辑。 注意:以上代码和配置仅供参考,实际开发中需要根据具体情况进行调整。 相关问题: 1. 如何测试开机自启动功能? 2. 在Android 11上,如何引导用户设置自启动权限? 3. 如何将应用设置为默认桌面应用? 4. 开机自启动后,如何避免重复启动多个Activity实例? 5. 在Android 10及以上版本,后台启动Activity有哪些限制?如何解决?</think>### 在 Android 11 中将 APP 设置为桌面应用并启用开机自启动 #### 一、设置桌面应用(Launcher) 1. **修改 AndroidManifest.xml** 在需要作为桌面Activity 中添加以下 intent-filter: ```xml <activity android:name=".YourLauncherActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> ``` - `CATEGORY_HOME` 声明该 Activity桌面入口 - 用户首次按 Home 键时,系统会弹出选择框让用户选择默认桌面应用[^4] 2. **处理默认桌面设置** 引导用户通过系统设置将应用设为默认桌面: ```java Intent intent = new Intent(Settings.ACTION_HOME_SETTINGS); startActivity(intent); ``` #### 二、启用开机自启动 1. **添加权限声明** 在 `AndroidManifest.xml` 中添加: ```xml <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> ``` 2. **注册广播接收器** 创建接收开机广播的 Receiver: ```xml <receiver android:name=".BootReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> <!-- 部分厂商需要 --> </intent-filter> </receiver> ``` 3. **实现广播接收器** ```java public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { Intent launcherIntent = new Intent(context, YourLauncherActivity.class); launcherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(launcherIntent); } } } ``` #### 三、Android 11 特殊处理 1. **解决系统限制问题** Android 11 默认禁止第三方应用自启动,需额外操作: - 引导用户在 **设置 → 应用 → 特殊应用权限 → 开机启动** 中手动启用权限 - 部分厂商(小米、华为等)需额外在系统管家/电池优化中允许自启动 2. **应对后台启动限制** 在 Activity 启动时添加标志位: ```java intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); ``` #### 四、完整流程示例 1. 用户安装应用后,首次按 Home 键选择该应用作为桌面 2. 用户手动在系统设置中授予开机启动权限 3. 设备重启后: - 系统发送 `BOOT_COMPLETED` 广播 - `BootReceiver` 启动桌面 Activity - 应用自动进入桌面模式 > **注意**:在 Android 11 上,部分设备可能需要额外修改系统源码才能完全解除限制(如修改 `ActivityStarter.java` 中的权限检查逻辑)[^1][^3]。 --- ### 相关问题 1. 如何在 Android 11 上检测应用是否被授予开机自启动权限? 2. 如何解决国产定制 ROM(如 MIUI、EMUI)对开机自启动的额外限制? 3. 当应用被设置为桌面应用时,如何接管系统返回键和最近任务键? 4. Android 11 后台启动 Activity 有哪些例外情况? 5. 如何避免开机自启动时重复创建多个桌面实例? [^1]: 需修改系统源码解除限制(如 `ActivityStarter.java` 中的权限检查) [^2]: 广播接收器需正确声明 `BOOT_COMPLETED` 过滤器 [^3]: 系统服务启动后发送广播实现自启动 [^4]: 通过 `CATEGORY_HOME` 声明桌面入口,用户首次需手动选择
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值