系列文章:
- Android Activity生命周期,启动模式,启动过程详解
- Android Activity启动模式图解
- Android从开机到点击Icon中间发生了什么?
前言
上文Android Activity生命周期,启动模式,启动过程详解我们讲解了Activity的生命周期,以及Activity启动过程的详细步骤,Activity承载着App对用户的内容展示,当我们在手机桌面上点开一个图标便打开了一个App,App上显示的界面对应背后的Activity,那么为什么点击图标便能打开App呢,其实桌面应用程序(Launcher)我们也可以理解为一个App,此Activity上承载着各种App图标,点击图标便有一个响应事件,这个响应事件就是打开图标对应的App。那么Launcher程序又是如何启动的呢?自然是和手机开机有关,因为我们知道手机开机后便是默认桌面,所以本文我们将从手机开机说起,梳理一下从手机开机,到启动Launcher程序,到点击Icon打开APP这个过程,下面分别介绍以上三个过程。
本文源码基于Android-25版本
开机过程分析
在看Android系统启动流程前,我们先了解下计算机PC启动过程,大概分为以为四步:
- BIOS:主要做硬件自检,转移控制权等工作;
- 主引导记录:BIOS把控制权转交给排在第一位的储存设备(主引导记录),共512字节;
- 硬盘启动:计算机的控制权由硬盘的某个分区控制;
- 操作系统:计算机的控制权转交给操作系统,内核首先被载入内存,以Linux为例,其第一个运行的程序是/sbin/init。它根据配置文件产生init进程,init是Linux启动后的第一个进程,pid进程编号为1,其他进程都是它的后代。然后,init线程加载系统的各个模块,比如窗口程序和网络程序,直至执行/bin/login程序,跳出登录界面,等待用户输入用户名和密码。
以上摘自 计算机是如何启动的?[阮一峰]
因为Android系统是基于Linux内核的,所以系统的开机过程肯定涉及到Linux内核的启动。但是Android系统属于嵌入式系统,没有计算机那样的BIOS引导程序,取而代之的是系统引导Bootloader,为启动系统内核做好准备。Android中也没有硬盘,取而代之的是ROM,类似硬盘存放操作系统和应用程序等。整体开机流程如下图所示:
第一步:上电复位
开机时,通电产生一个CPU复位信号,CPU开始执行指令,第一条指令是设定好的(固化在ROM上),将引导程序(Bootloader)加载到RAM中;
第二步:Bootloader
Bootloader启动,引导进入Linux内核;引导程序是运行的第一个程序,不同的主板和芯片具有不同引导程序,不同的手机厂商使用不同的程序,这部分不属于Android操作系统,因此可能是厂商加锁的地方;
第三步:Linux内核
Linux内核启动后主要做一些初始化工作,比如初始化软硬件环境,加载驱动程序,挂载根文件系统,内核加载的最后阶段启动第一个进程init进程;
第四步:init进程
init进程(/system/core/init/*)是系统第一个进程,进程号为1,该进程会首先加载一个init.rc配置文件,init.rc文件是Android系统的重要配置文件,位于(/system/core/rootdir/init.rc),其主要功能是定义了系统启动时需要执行的一系列动作,设置环境变量,生成系统运行所需要的文件或目录,执行特定Services等。
Android针对init.rc有特定的格式和规则,它由Android Init Language语言编写而成,Android Init Language主要包含四种声明:Actions(动作),Commands(命令),Services(服务),Options(选项)。
通过init.rc脚本主要启动了以下服务:
Action/Service | 描述 |
---|---|
on early-init | 设置init进程以及它创建的子进程的优先级,设置init进程的安全环境 |
on init | 设置全局环境,为cpu accounting创建cgroup(资源控制)挂载点 |
on fs | 挂载mtd分区 |
on post-fs | 改变系统目录的访问权限 |
on post-fs-data | 改变/data目录以及它的子目录的访问权限 |
on boot | 基本网络的初始化,内存管理等等 |
service servicemanager | 启动系统管理器管理所有的本地服务,比如位置、音频、Shared preference等等… |
service zygote | 启动zygote作为应用进程 |
本表来自Android启动过程深入解析
init.rc最重要的的任务是启动一个Zygote(孵化器)进程,此进程负责启动Android应用进程的启动工作。init.rc通过include引入init.zygote.rc创建Zygote进程。
init.rc的具体语法可以参考深入分析AIL语言及init.rc文件
第五步:Zygote进程
Zygote进程孵化了所有Android应用进程,是Android Framework的基础。在Java中,不同的虚拟机实例会为不同的应用分配不同的内存。Android应用程序是运行在Dalvik虚拟机里面的,并且每一个应用程序对应有一个单独的Dalvik虚拟机实例。但是Android应用程序中的Dalvik虚拟机实例实际上是从Zygote进程的地址空间拷贝而来的,这样就可以加快Android应用程序的启动速度,Zygote让Dalvik虚拟机共享代码,低内存占用,最小启动时间成为可能。
Zygote其实是一个虚拟机进程,它会完成虚拟机的初始化、库的加载、预制类库、核心类库的初始化等,当系统需要一个新的虚拟机时,它会迅速复制自己,提供给系统。每当我们打开一个新的APP时都会fork之前的Zygote进程。下面介绍下Zygote启动过程:
App_main.main()
此函数位于(frameworks/base/cmds/app_process/App_main.cpp)中,主要添加了Android运行环境,即创建一个AppRuntime变量runtime,而AppRuntime继承自AndroidRuntime,在App_main.main()方法中执行了runtime.start()方法,也就是执行了AndroidRuntime类的start方法,由于在init.rc文件中设置了启动app_process的参数为:--zygote
和--start-system-server
,因此实际上在main函数里最终会执行下面语句:
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
AndroidRuntime.start()
此函数位于(frameworks/base/core/jni/AndroidRuntime.cpp)中,主要做了以下三件事情:
- startVm():创建虚拟机,主要是关于虚拟机参数的设置;
- startReg(): 注册JNI方法;
- env->GetStaticMethodID(): 通过JNI调用Java函数,进入Java代码中,实际上最终调用了com.android.internal.os.ZygoteInit的main函数。
ZygoteInit.main()
此函数位于(frameworks/base/core/java/com/android/internal/os/ZygoteInit.java)中:
public static void main(String argv[]) {
try {
......
String socketName = "zygote";
......
registerZygoteSocket(socketName);
preload();
......
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
......
} catch (RuntimeException ex) {
......
}
}
主要做了以下几件事情:
- registerZygoteSocket():创建一个名为Zygote的socket接口,用来和ActivityManagerService通讯
- preload():预加载通用类等资源,如res(drawable,xml信息,strings)等
- startSystemServer(): 启动SystemServer等服务,在此函数内部Zygote进程通过Zygote.forkSystemServer函数来创建一个新的进程来启动SystemServer组件,如下所示:
private static boolean startSystemServer(){
....
// SystemServer是由Zygote通过Zygote.forkSystemServer函数fork出来的
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
....
// 子进程返回0,即SystemServer
if (pid == 0) {
....
handleSystemServerProcess(parsedArgs);
}
}
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
// 关闭这里继承来的socket,因为这里的子进程并不会使用到socket
closeServerSocket();
// 继续启动SystemServer的操作
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
/* should never reach here */
}
public static final void zygoteInit(String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
....
// 初始化Binder进程间通信机制
zygoteInitNative();
....
}
- runSelectLoopMode(): 轮询监听socket,不断处理来自客户端的AMS请求,然后交给runOnce()处理
至此,Zygote进程就启动完成了,总结如下:
- init进程创建Zygote进程,而Zygote负责整个后续Android 应用程序的创建
- Zygote进程启动时会创建SystemServer进程,SystemServer进程负责启动系统的关键服务,如ActivityManagerService,PowerManagerService,PackageManagerService等
- 当我们准备启动一个APP时,AMS通过Socket和Zygote进程间通信,通知Zygote fork子进程,加载需要的类。
整体过程如下图所示:
SystemServer启动过程
上述启动Zygote进程的过程中,我们提到了启动SystemServer这个Android系统核心进程,为了更清楚理解Android系统工作,这里我们再将启动SystemServer进程的过程单独列为一小节说明。
SystemServer由Zygote fork而成,进程名为system_server,承载着framework服务。
具体的SystemServer启动过程较为复杂,通过一系列方法最终转移到SystemServer类的main()方法中,如下图所示:
图片来自 作者:Gityuan博客 来源:Android系统启动-SystemServer上篇
链接:http://gityuan.com/2016/02/14/android-system-server/
SystemServer.main()方法主要的流程如下:
SystemServer.main() // 初始化SystemServer对象,再调用run()方法
SystemServer.run() // 调用以下方法
createSystemContext() // 创建system_server进程的上下文信息
startBootstrapServices(); // 创建AMS,PMS,LightService,DisplayManagerService等服务
startCoreServices(); // 启动BatteryService,UsageStatsService,WebViewUpdateService服务
startOtherServices(); // 显示启动界面,调用AMS.systemReady()方法
Looper.loop(); // 开启消息循环
在上面的startOtherServices()方法中有一段代码如下:
mActivityManagerService.systemReady(new Runnable() {
public void run() {
...
}
});
public final class ActivityManagerService{
public void systemReady(final Runnable goingCallback) {
....
startHomeActivityLocked(mCurrentUserId, "systemReady"); //启动桌面
mStackSupervisor.resumeTopActivitiesLocked(); //恢复栈顶的Activity
}
}
AMS的systemReady()方法中启动了WebView,SystemUI,开启WatchDog,启动桌面Launcher 程序。
启动Launcher的过程如下:首先通过Zygote进程fork一个子进程作为APP进程,然后创建Application,再启动Activity,最后显示出实际画面。
完整的启动过程如下:
本图片摘自 作者: Jeanboydev 来源:CSDN
链接 https://blog.csdn.net/freekiteyu/article/details/79318031
Launcher
从上面分析我们知道,AMS在完成服务注册等初始化工作后,在main()方法中调用systemReady()方法,进而调用startHomeActivityLocked()来启动Launcher。
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
return false;
}
// 获取启动Launcher的intent信息
Intent intent = getHomeIntent();
// 通过PackageManagerService获得Launcher的Activity描述信息Info
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
启动Launcher的Intent对象中添加了Intent.CATEGORY_HOME常量,代表将要启动Home界面。
获得intent和ActivityInfo后,后续方法调用顺序为:
ActivityStarter.startHomeActivityLocked()
ActivityStackSupervisor.moveHomeStackTaskToTop() // 把Launcher的堆栈移到顶部
ActivityStarter.startActivityLocked()
......
进入ActivityStarter.startActivityLocked()方法后,后续过程就和启动一个普通的Activity没什么区别,启动一个普通Activity的过程可以参考我的上一篇文章Android Activity生命周期,启动模式,启动过程详解。
第一次启动Launcher时,执行到ActivityStackSupervisor的startSpecificActivityLocked()方法时,会判断Launcher Activity所在进程是否已经存在,如果不存在则会创建一个进程容纳Activity,创建进程的流程就是通过AMS向Zygote发起请求,Zygote收到请求后fork一个子进程,然后再继续启动Launcher。创建子进程的过程,我们会在下文中点击Icon启动一个APP的过程中进行分析。
总而言之,Launcher的启动过程就是类似一个启动Activity的过程。但是,我们知道Launcher上放着各种应用程序图标,有着不同的状态,因此其Activity实现具体原理还可以进一步探究。可以参考Android Launcher加载流程源码分析 、 Android M Launcher3主流程源码浅析、Android系统启动流程(四)Launcher启动过程与系统启动流程
应用安装的时候,通过PackageManagerService解析apk的AndroidManifest.xml文件,提取出这个apk的信息写入packages.xml中,包括权限、包名、icon、apk安装位置、版本、userID等信息,Launcher为已安装的程序在桌面上生成图标,点击图标便可启动应用,下面分析启动应用的过程。
本段来自 Jeanboydev CSDN博客 一篇文章看明白 Android 从点击应用图标到界面显示的过程
点击Icon启动APP
其实点击Icon启动APP的过程就是启动APP主Activity的过程,即启动MainActivity的过程,从而把APP启动起来,但是当我们新启动一个APP过程时,需要判断此APP所在进程是否已经存在,如果不存在则需要fork一个子进程。下面详细介绍这个具体过程。
Launcher.startActivitySafely
点击图标时,桌面程序Launcher.java做出响应,调用Launcher.startActivitySafely来启动一个Activity
public class Launcher extends Activity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
AccessibilityManager.AccessibilityStateChangeListener {
......
public void onClick(View v) {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
onClickAppShortcut(v);
}
......
}
protected void onClickAppShortcut(final View v) {
......
startAppShortcutOrInfoActivity(v);
}
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
Intent intent = item.getIntent();
......
boolean success = startActivitySafely(v, intent, item);
......
}
public boolean startActivitySafely(Intent intent, Object tag) {
......
// 表示在新的Task中启动此Activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
......
try {
......
startActivity(intent);
} catch (ActivityNotFoundException e) {
......
} catch (SecurityException e) {
......
}
}
......
}
Activity.startActivity
Launcher继承自Activity,因此上段代码中startActivity(intent)便调用了Activity类的该方法:
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
Activity的StartActivity()方法最终调用了startActivityForResult()方法,这和我之前一篇文章Android Activity生命周期,启动模式,启动过程详解相同,后续过程基本没有区别。和前文类似,当执行到ActivityStackSupervisor的startSpecificActivityLocked()方法时,会判断APP所在进程是否已经存在,如果不存在则会fork一个进程。下面介绍fork新进程的过程:
fork新进程
Android创建进程的流程图大概如下:
图片来自 作者:Gityuan博客 来源:理解Android进程创建流程
链接:http://gityuan.com/2016/03/26/app-process-create/
ActivityManagerService.startProcessLocked()
ActivityStackSupervisor的startSpecificActivityLocked()的方法中有下面一段代码
ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true);
第一次启动APP时,很明显上面获得的变量app=null,在配置文件AndroidManifest.xml中我们如果没有指定process属性,系统会默认使用package的名称当做process名。而且每一个程序都有自己的uid,uid+process的组合就可以为每一个应用程序创建一个ProcessRecord,每次新建进程前都会判断此ProcessRecord是否存在,如果已经存在则不会新建进程。
如果不存在,则新建进程:
mService.startProcessLocked(r.processName, r.info.applicationInfotrue, 0,
"activity", r.intent.getComponent(), false, false, true);
因为startProcessLocked有多个重载形式,上述方法最终调用的方法为:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
......
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
......
}
最终转到Process的start()方法中。
Process.start()
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
......
}
}
又继续调用Process.startViaZygote()方法:
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
......
// openZygoteSocketIfNeeded(abi)方法是根据当前的abi来选择与zygote还是zygote64来进行通信
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
继续调用zygoteSendArgsAndGetResult()方法:
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList args)
throws ZygoteStartFailedEx {
try {
......
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
ProcessStartResult result = new ProcessStartResult();
// 等待Zygote的socket返回新进程的pid
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
通过socket向Zygote发送一系列参数,然后进入阻塞等待状态,直到远端的socket服务端发送回来新创建的进程pid才返回。Zygote收到请求后,开始工作,我们又回到了ZygoteInit.main()方法中。
ZygoteInit.main()
ZygoteInit.main()方法中主要停留在runSelectLoop函数中,等待AMS的请求,收到请求时会调用ZygoteConnection的runOnce函数来处理请求,后序函数调用逻辑为:
ZygoteConnection.runOnce()
Zygote.forkAndSpecialize() // fork当前进程来创建一个子进程
ZygoteConnection.handleChildProc() // 启动上面fork的子进程,并切换到子进程中执行后续代码
RuntimeInit.zygoteInit() // 创建Binder线程池,一些初始化工作
RuntimeInit.invokeStaticMain() // 利用反射ActivityThread.main()方法
ActivityThread.main() // 创建消息循环,进入消息循环状态
ActivityThread.main()
public static void main(String[] args) {
......
// 创建消息循环
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// 进入消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
此时只创建了应用程序的 ActivityThread 和 ApplicationThread,和开启了 Handler 消息循环机制,其他的都还未创建, ActivityThread.attach(false) 又会最终到 ActivityMangerService 的 attachApplication,这个方法其实是将本地的 ApplicationThread 传递到 ActivityMangerService。然后 ActivityMangerService 就可以通过 ApplicationThread 的代理 ApplicationThreadProxy 来调用应用程序 ApplicationThread.bindApplication,通知应用程序的 ApplicationThread 已和 ActivityMangerService 绑定,可以不借助其他进程帮助直接通信了。此时 Launcher 的任务也算是完成了。
本段来源
作者:Jeanboydev
来源:CSDN
原文:https://blog.csdn.net/freekiteyu/article/details/79318031
下面看下ActivityMangerService的attachApplication方法:
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
......
attachApplicationLocked(thread, callingPid);
......
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
// 检查顶层Activity是否等待被运行
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
// 寻找所有需要在该进程中运行的服务
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
// 检查是否在这个进程中有下一个广播接收者
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app);
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
......
return true;
}
当发现有Activity需要被启动时,调用ActivityStackSupervisor.attachApplicationLocked()方法:
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
......
realStartActivityLocked(hr, app, true, true);
......
}
后续具体的启动步骤又回到了启动一个普通Activity的过程中,同样可以参考上篇文章Android Activity生命周期,启动模式,启动过程详解。
最后,再引用一张图片结束本文:
图片来自 Jeanboydev 来源:CSDN
链接:https://blog.csdn.net/freekiteyu/article/details/79318031
至此,我们基本把Android从开机到点击Icon的过程理清楚了,但是中间每一部分都有很多细节值得深入,因此本文只给了一个大概流程,具体的实现细节可以分模块去查找探究,本文结束!
参考信息
- 计算机是如何启动的?
- Android - 系统启动过程
- 深入分析AIL语言及init.rc文件
- [Android6.0] 启动流程分析(从开机到第一个程序)
- Android系统启动-zygote篇
- Android系统启动-SystemServer上篇
- Android系统启动-SystemServer下篇
- ActivityManagerService启动过程
- Android系统进程Zygote启动过程的源代码分析
- Android应用程序进程启动过程的源代码分析
- Android应用程序启动过程源代码分析
- 理解Android进程创建流程
- Android应用程序进程启动过程(前篇)
- Android点击应用Icon发生了什么
- 一篇文章看明白 Android 从点击应用图标到界面显示的过程