前言:

百度一搜能找到很多讲APP启动流程的,但是往往要么就是太老旧(还是基于android6去分析的),要么就是不全(往往只讲了整个流程的一小部分)。所以我结合网上现有的文章,以及源码的阅读和调试,耗费了3整天的时间,力求写出一篇最完整,最详细,最通俗易懂的文章,来讲清楚在android上,APP是如何启动的。

该文属于安卓源码探究专栏中的文章,专栏中很多类似源码分析的文章,欢迎大家阅读。

链接如下:

安卓源码研究

一、APP启动流程概览

涉及到四个进程之间的通信,分别是Laucher进程(桌面APP),SystemServer进程(AMS所属进程),Zygote进程(系统和所有APP的创建进程),APP进程。

APP简要的启动流程是这样的:
1.Laucher进程会通过binder的方式通知SystemServer进程。

2.然后SystemServer进程中的AMS会查询对应的Activity栈信息,如果对应APP进程不存在则会加载占位图。然后通过socket的方式通知Zygote去创建APP进程。

3.APP进程创建后会执行main方法,然后通知AMS

4.AMS收到信息后会继续通知APP去创建Application,并且接下来会通知APP进程取拉起Activity。

5.APP进程依次收到通知后,会依次完成加载APK,初始化Application,执行Activtiy生命周期等操作。最终会把首屏展示出来。

流程图如下,建议双击放大后观看:

接下来的几章,会按照上面的流程逐一拆解分析:

二、Launcher通知AMS启动APP

Launcher进程其实和普通APP是一样的,甚至我们可以把自己的APP设置为桌面APP。

2.1 Launcher获取到AMS的binder

而Launcher通知AMS的流程和正常APP也是一样的,通过ServiceManager获取到AMS的binder引用。这里提到了ServiceManager,其实ServiceManagerService也是单独的一个进程,其存储了所有APP所需要的binder引用。而且其地址是固定的,所以获取ServiceManager可以直接获取。

2.2 通知ActivityManagerService

Launcher通过上述获取到的binder通知到AMS,调用的方式是startActivityWithFeature。

而由于AMS实现了IActivityManager.Stub的实现,所以其startActivityWithFeature方法会收到launcher发过来的通知。

2.3 系统进程加载占位图

AMS中,会交由ActivityTaskManagerService去进行对应启动任务的处理。最终会交给ActivityStart进行处理。

ActivityStart中,首先会进行一个逻辑判断,如果进程不存在,则首先会加载APP中MainActivity的主题作为背景图,显示到屏幕上。这一步操作是发生在SystemServer进程的,APP进程还未创建。

android11开始支持启动动画,逻辑也是在这里处理的。

2.4 AMS进行启动操作

一个进程中会有多个任务栈,栈对应的是Task类。

一个任务栈中会有多个Activity对象,这个Activity对象在AMS中使用ActivityRecord记录。

这一块的具体逻辑,我会单独写一篇Activity的启动逻辑来进行描述。

这里暂时先简单描述下,如果AMS发现进程不存在,会去通知Zygote的进行进程fork就好,对应的fork逻辑在下一章。

三、Zygote创建APP进程

3.1 AMS中内部逻辑执行

ActivityTaskManagerService调用startProcessAsync方法会去负责创建APP进程,这是异步的,通过handler转发后最终会调用到LocalService.startProcess()方法。

然后会通知到ProcessList.startProcessLocked方法,这个方法中,会构造一个对象ProcessRecord对象,然后把各种信息添加到这个对象中。

接下来会调用到ProcessList.startProcessLocked这个方法,这个方法主要是负责把各种信息转换为runtimeFlags标记位,连同上面构造的ProcessRecord继续传入下一层。

最终通知到Process.start方法,然后交由ZygoteProcess.start方法,最终传递到startViaZygote方法。

3.2 请求参数拼接成字符串发送给Zygote

startViaZygote这个方法中,会把各种配置参数拼接为字符串。

最终在ZygoteProcess.attemptUsapSendArgsAndGetResult方法中,通过LocalSocket的方式把上面拼接的字符串内容传递给Zygote进程。并且从socket中读取返回值,返回值的PID>0则证明进程创建成功。

3.3 Zygote进程逻辑

在了解Zygote进程如何解析AMS发送过来的请求之前,我们先简单了解下Zygote进程创建后的一些基本逻辑。如下图:

​​​​​​​

一。Zygote进程创建后,最开始的入口是在C层,app_main.cpp文件的main方法(该进程由Init进程启动,这里就不扩展了),在main方法中会配置一些JVM的参数,这个和JAVA的虚拟机参数配置类似。最终会调用到AndroidRuntime.cpp中的start函数,去启动JVM虚拟机。

 if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {return;}

二。虚拟机创建之后,会通过native层反射找到main函数,并调用ZygoteInit类的main函数。

AndroidRumtime.cpp的start方法中:

if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}

三。ZygoteInit类的main方法中,会做如下几件事:

第一步,把自身的进程ID设置为0,并且没有parent进程。

第二步,会进行一系列的初始化操作,比如加载native环境,加载JVM环境,加载系统类,加载系统资源等等。都在其preload方法中。

第三步,如果是首次执行,则会创建SystemServer进程。这也是Zygote进程的大儿子。AMS,WMS都属于SystemServer进程。

第四步,会创建zygoteServer对象,并且调用其runSelectLoop方法。监听socket不断的接传递过来的信息

第五步,Zygote进程的fork,其实是复制一个原原本本的自己。runSelectLoop方法中其实会去执行fork操作,这个后面会讲,我们这里只需要知道,执行到caller.run();这一句的时候,已经处于APP进程状态了。

public static void main(String[] argv) {ZygoteServer zygoteServer = null;// Mark zygote start. This ensures that thread creation will throw// an error.ZygoteHooks.startZygoteNoThreadCreation();// Zygote goes into its own process group.try {Os.setpgid(0, 0);} catch (ErrnoException ex) {throw new RuntimeException("Failed to setpgid(0,0)", ex);}...if (!enableLazyPreload) {bootTimingsTraceLog.traceBegin("ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());//2.初始化操作preload(bootTimingsTraceLog);EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());bootTimingsTraceLog.traceEnd(); // ZygotePreload}// Do an initial gc to clean up after startupbootTimingsTraceLog.traceBegin("PostZygoteInitGC");gcAndFinalize();bootTimingsTraceLog.traceEnd(); // PostZygoteInitGCbootTimingsTraceLog.traceEnd(); // ZygoteInitZygote.initNativeState(isPrimaryZygote);ZygoteHooks.stopZygoteNoThreadCreation();zygoteServer = new ZygoteServer(isPrimaryZygote);if (startSystemServer) {//3.首次启动时,会启动系统进程Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);// {@code r == null} in the parent (zygote) process, and {@code r != null} in the// child (system_server) process.if (r != null) {r.run();return;}}//4.启动无限循环监听socketcaller = zygoteServer.runSelectLoop(abiList);} catch (Throwable ex) {Log.e(TAG, "System zygote died with fatal exception", ex);throw ex;} finally {if (zygoteServer != null) {zygoteServer.closeServerSocket();}}// We're in the child process and have exited the select loop. Proceed to execute the// command.if (caller != null) {//5.这里的调用已经是在APP进程了,zygote进程永远不会执行到这里caller.run();}}

3.4 收到通知后去fork产生APP进程

runSelectLoop方法中,会开启一个无限循环。如果收到了消息

如果收到了消息,则会调用ZygoteConnection.processCommand去处理。

Runnable runSelectLoop (String abiList){
//        ...while (true) {
//            ...try {ZygoteConnection connection = peers.get(pollIndex);boolean multipleForksOK = !isUsapPoolEnabled()&& ZygoteHooks.isIndefiniteThreadSuspensionSafe();//收到消息,处理消息并且返回runnable。final Runnable command =connection.processCommand(this, multipleForksOK);// TODO (chriswailes): Is this extra check necessary?if (mIsForkChild) {if (command == null) {throw new IllegalStateException("command == null");}//子进程执行,子进程的mIsForkChild会被设置为true,则返回commandreturn command;} else {//Zygote进程执行,则继续执行循环//       ...}}}}

而在processCommand中,会解析收到的参数,最终调用Zygote.forkAndSpecialize去fork一个新进程。这个方法虽然只会调用一次,返回因为进程是拷贝的,所以实际上会有两次返回,返回两个pid。pid为0时为子进程,设置标记为子进程。反之仍就还是Zygote进程。

 pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,parsedArgs.mBindMountAppStorageDirs);try {if (pid == 0) {// in childzygoteServer.setForkChild();zygoteServer.closeServerSocket();IoUtils.closeQuietly(serverPipeFd);serverPipeFd = null;return handleChildProc(parsedArgs, childPipeFd,parsedArgs.mStartChildZygote);} else {// In the parent. A pid < 0 indicates a failure and will be handled in// handleParentProc.IoUtils.closeQuietly(childPipeFd);childPipeFd = null;handleParentProc(pid, serverPipeFd);return null;}} finally {IoUtils.closeQuietly(childPipeFd);IoUtils.closeQuietly(serverPipeFd);}

3.5 调用ActivityThread.main方法

调用到handleChildProc方法时,已经处于APP进程的状态。

该方法掉调用到ZygoteInit.childZygoteInit方法,去调用ActivityThread的main方法。

  static Runnable childZygoteInit(String[] argv) {RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);return RuntimeInit.findStaticMain(args.startClass, args.startArgs, /* classLoader= */null);}

findStaticMain方法:

protected static Runnable findStaticMain(String className, String[] argv,ClassLoader classLoader) {Class<?> cl;try {cl = Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {throw new RuntimeException("Missing class when invoking static main " + className,ex);}Method m;try {m = cl.getMethod("main", new Class[] { String[].class });} catch (NoSuchMethodException ex) {throw new RuntimeException("Missing static main on " + className, ex);} catch (SecurityException ex) {throw new RuntimeException("Problem getting static main on " + className, ex);}int modifiers = m.getModifiers();if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {throw new RuntimeException("Main method is not public and static on " + className);}/** This throw gets caught in ZygoteInit.main(), which responds* by invoking the exception's run() method. This arrangement* clears up all the stack frames that were required in setting* up the process.*/return new MethodAndArgsCaller(m, argv);}

(PS:这里只看到了main方法,没有看到ActivityThread类。其实ActivityThread类是通过socket传递过来的一个参数,其定义在ProcessList的startProcessLocked方法中)

所以最后返回的其实是一个runnable接口实现类,而这个runnable中实现了调用ActivityThread中main方法的逻辑。

还记得3.3中Zygote创建后的逻辑吗?最后一句是调用 caller.run();

是的,这个caller就是最后返回的runnable接口实现类,去完成调用main方法的操作。

四、APP进程创建初始化操作

app进程初始化的操作比较简单,主要做了两件事,初始化looper,以及通知AMS。(注意,这里只是APP进程创建了,但是还没有加载APK中的任何类)

4.1 初始化MainLooper

调用main方法的线程,会被设置为主线程,Looper此时会开启无限循环。

main(){//looper绑定主线程Looper.prepareMainLooper();//通知AMSActivityThread thread = new ActivityThread();thread.attach(false, startSeq);//looper开启无限循环读取消息Looper.loop();
}

4.2 通知AMS

ActivityThread.attach方法中,实现逻辑也是比较简单的,直接通过binder通知AMS,并且把自身的binder(ApplicationThread)也传递给AMS。

            final IActivityManager mgr = ActivityManager.getService();try {mgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}

这里AMS的binder是直接通过ServierManager的方式获取的。ServierManager存储了所有的binder引用,注册的形式,AMS在启动的时候去注册。

五、AMS通知APP进程进行各种生命周期操作

5.1 唤起APP初始化并拉起APP首屏

ActivityManagerService的attachApplication方法会收到APP传递过来的消息,然后交由attachApplicationLocked处理。

attachApplicationLocked中主要负责两件事:

1.通知APP进程进行初始化操作;

2.进行一些列操作,最终通知APP拉起指定的MainActivity。

说到这,问一个问题1,为什么明明是串行通知APP去执行的,而APP那边不会出现先加载Activity,再去初始化应用的情况呢?答案在下一小节。

 @GuardedBy("this")private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {...if (app.getIsolatedEntryPoint() != null) {...} else if (instr2 != null) {//1通知APP进行初始化操作thread.bindApplication(processName, appInfo, providerList,instr2.mClass,profilerInfo, instr2.mArguments,instr2.mWatcher,instr2.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.getCompat(), getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.getDisabledCompatChanges(), serializedSystemFontMap);} else {...同上}...// See if the top visible activity is waiting to run in this process...if (normalMode) {try {//拉起didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}...return true;}

5.2 APP进行初始化操作

ActivityThread中ApplicationThread的bindApplication会收到通知,通过handler交给主线程去处理。所以我们也就知道上面问题1的答案了,无论是初始化APP,还是拉起Activity,都是最终交给Handler切换到主线程处理的。所以哪怕初始化APP是耗时操作,拉起Activity的任务也得排队等到前面任务执行完了才能执行。

最终通过handler是交给handleBindApplciation去完成APP的初始化逻辑的。主要包含下面几个操作:

1.使用classLoader去加载APK中的DEX文件。

2.加载APK中的资源。

3.反射生成Application类,并调用其attachBaseApplication方法。

4.调用Application的onCreate方法。

@UnsupportedAppUsageprivate void handleBindApplication(AppBindData data) {//1.classLoader加载APK中的dex,并且加载APK的资源final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);// Continue loading instrumentation.//2.生成代理类mInstrumentation = new Instrumentation();mInstrumentation.basicInit(this);// Allow disk access during application and provider setup. This could// block processing ordered broadcasts, but later processing would// probably end up doing the same disk access.Application app;//3.声明applicationapp = data.info.makeApplication(data.restrictedBackupMode, null);...//4.调用Application的onCreatemInstrumentation.onCreate(data.instrumentationArgs);}

onCreate创建完成后并不会通知AMS,因为activity的拉起操作和初始化Application在AMS中是串行的。

5.3 AMS中处理Activity启动逻辑

5.1中讲到attachApplicationLocked会最终通知APP去拉起Activity,那么整个流程是怎样的呢?

会执行下面这样的调用顺序。

ActivityManagerService.attachApplicationLocked->

ActivityTaskManagerService.LocalService.attachApplication->

RootWindowContainer.attachApplication->

RootWindowContainer.startActivityForAttachedApplicationIfNeeded->

ActivityTaskSupervisor.realStartActivityLocked

到了realStartActivityLocked这一步,正好对应2.4所讲的。APP进程存在的也会调用这个方法,而不存在则先创建进程,最终也会执行到这一步。

realStartActivityLocked中创建Activity的生命周期事务,最终通过ClientLifecycleManager.scheduleTransaction通过binder发送到APP进程的ApplicationThread.scheduleTransaction方法中,则AMS流程就完成了。

5.4 APP完成Activity的启动

生命周期事务是安卓8.0之后出现的,简单来说就是之前的模式是:

AMS发一个协商好消息,APP收到后,根据消息内存来决定自己去做操作;

而事务模式下,AMS发送一系列事务到APP进程,APP收到后,直接去执行这一系列的事务。而这些事务就是activity的生命周期调用。

android源码学习- APP启动流程(android12源码)相关推荐

  1. 基于Android实现日语学习app设计与实现演示【附项目源码+论文说明】分享

    基于Android实现日语学习app设计与实现演示 摘要 随着手机使用的普及,人们获取与保存信息的方式已经发生了激动人心的转变.智能手机正在逐步融入我们的生活,并影响和改变着我们的生活.由于现在各种智 ...

  2. 基于Android实现日语学习app设计与实现演示【附项目源码+论文说明】

    基于Android实现日语学习app设计与实现演示 摘要 随着手机使用的普及,人们获取与保存信息的方式已经发生了激动人心的转变.智能手机正在逐步融入我们的生活,并影响和改变着我们的生活.由于现在各种智 ...

  3. Android App启动流程详解

    前言:在之前的文章中已经写了apk的打包流程.安装流程,今天就是梳理一下apk系列的最后的流程--app启动流程.经过今天的梳理以后咱们就可以对apk包是怎么编译生成的.apk是怎么被安装到安卓手机的 ...

  4. 【安卓 R 源码】Activity 启动流程及其生命周期源码分析

    1. Activty 的生命周期 activity的生命周期 oncreate()->onstart()->onResume()->onPause()->onStop()-&g ...

  5. Android入门之APP启动流程

    俗话说,要想优化好,流程不可少.作为一款App的开发者,首先要把它的启动流程做好了,简单明了的启动流程不仅拥有很好的体验感,还能获得更多用户对App的肯定.本篇文章就带大家了解下app启动流程的三个进 ...

  6. bluetoothd源码剖析(一)启动流程

    蓝牙系列: bluez调试笔记_weixin_41069709的博客-CSDN博客_bluezbluez移植https://blog.csdn.net/weixin_41069709/article/ ...

  7. 【Android】之【App启动】

    一.启动方式 Android 应用的启动方式大概分为热启动.冷启动.温启动三种,关于冷启动.热启动.温启动三者启动方式对比可以参考下面的流程图学习. 1.1 冷启动 冷启动具有耗时最多,衡量标准的特征 ...

  8. 深入分析Android 9.0源代码——Service启动流程(startService方式)

    引言 点击此处查看<深入分析Android 9.0源代码>系列的组织结构和相关说明. 1 应用进程发起启动请求 本章的调用流程如下图所示: (Context)ContextWrapperC ...

  9. 解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

最新文章

  1. 计算机编程导论python程序设计答案-学堂在线_计算机科学与Python编程导论_作业课后答案...
  2. stm32串口传输数据第一个数据被吞_stm32串口发送数据复位 第一个数据丢失
  3. maya崩溃自动保存路径_maya 使用swig将插件编译成pyd,无缝使用内置数据实现加速计算模块...
  4. 教你设置eclipse自动生成的author等注释
  5. springboot监控服务器信息,面试官:聊一聊SpringBoot服务监控机制
  6. LeetCode 1056. 易混淆数(哈希)
  7. packetbeat mysql_简单使用packetbeat
  8. Echarts数据可视化series-effectscatter特效散点图,开发全解+完美注释
  9. 调研时报表的相关信息
  10. Linux下运行黑客帝国屏保
  11. matlab对矩阵谱分解
  12. 人生得意须尽善,人生失意亦无怨;人生一世,草木一秋,乐也,悲也,何乐而不为呢?...
  13. wps可以登录网页版_wps网页版入口_WPS个人免费版_点点游
  14. 吃货必备手册,爱辣条就不能错过的零食地图
  15. ArcGIS Server 统计服务请求数等
  16. 什么是数据资产管理?5个角度帮你参透数据资产管理
  17. python locust在linux下的安装
  18. 数字图像处理第二章——数字图像基础
  19. 如何删除桌面的回收站图标
  20. 用计算机打字打错了怎么办,电脑键盘打字错乱,怎么快速解决?

热门文章

  1. Cocos2d-x 3.x 图形学渲染系列二
  2. poi-tl生成表单并转成pdf(Linux服务器)
  3. stream流详解(JDK1.8的特性)
  4. 程序员年终总结----git合入代码行数统计
  5. 右键收藏!2021 Google 开发者大会怎么看?
  6. 图神经网络如何构建超图HyperGraph
  7. Android 11 Recent按键流程
  8. tomcat配置虚拟主机(Host)
  9. bat route 循环
  10. WiFi 6/WiFi 6E 频段及BAND