面试必备:Android Activity启动流程源码分析
最近大致分析了一把 Activity 启动的流程,趁着今天精神状态好,把之前记录的写成文章。
开门见山,我们直接点进去看 Activity 的 startActivity
, 最终,我们都会走到 startActivityForResult
这个方法,我们可以发现关键的代码:
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);
我们会发现 Activity 启动其实都经过了一个中转站叫做 Instrumentation
, 查看Instrumentation
的 execStartActivity
方法:
/// 删除了我们不关心的部分
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
我们会发现这里通过 ActivityManager.getService
在进行通信,进去查看,我们发现这个 service 其实是一个 IActivityManager.aidl
, 说明这里我们进行了一次 Android 的 IPC。
全局搜索 extends IActivityManager
我们可以发现进行通信的就是 ActivityManagerService
, 查看 startActivity
最终可以走到 ActivityStart
的 startActivityMayWait
方法。我们抽取它的关键代码:
这部分我们可以看到根据 intent 解析除了需要的信息,并根据信息去获取了跳转 Activity 的系统权限。
这一部分代码,则对 intent 进行了处理和判断,我们基本可以省略这部分非关键逻辑
最终我们会走到 startActivityLocked
方法,并走到 startActivity
这里我们会看到很多对于不同的 ActivityManager
的 状态进行逻辑判断和处理,这里不影响我们的关键流程,我们可以继续往下分析, 分析 doPendingActivityLaunchesLocked
方法
startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null,
null, null /*outRecords*/);
最终还是会走到另一个重载的 startActivity
:
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
查看 startActivityUnchecked
: 这里代码逻辑比较长,我们查看 ActivityStackSupervisor
的.resumeFocusedStackTopActivityLocked
方法
继续查看 resumeTopActivityUncheckedLocked
方法, 跟踪到 resumeTopActivityInnerLocked
方法:
这边我们查看需要 restart 这个 Activity 的简单情况,会调用 ActivityStackSupervisor
的 startSpecificActivityLocked
方法
这里我们找到了逻辑的关键:如果 app的线程和进程都存在,我们会执行 realStartActivityLocked
方法。否则,会继续进行 IPC 通知 ActivityManagerService
去执行 startProcessLocked
这里我们差不多能猜到启动逻辑:
- 如果启动的是我们自己 app 进程的 Activity, 那么直接去启动就好了
- 如果我们启动的 Activity 所在的进程不存在,例如:我们把微信 kill 了,然后跳转微信分享的 Activity,或者我们点击launch 的微信图标,那么,我么就会走创建新进程的逻辑
那么我们分别来跟踪这2种情况:
启动自己的Activity
我们可以找到这段代码的关键逻辑,我们先分析下 app.thread
是什么。跟踪进去会发现是一个 IApplicationThread
, 可以发现这里又是一个 aidl, 最后我们可以找到 ApplicationThread
,
private class ApplicationThread extends IApplicationThread.Stub
这是 ActivityThread
的一个静态内部类,ActivtyThread和启动Activity 相关,那么这个类就应该是和 Application 启动相关。
我们会发现最后其实发了一个message 到消息队列中,找到 H
这个 handler 的 handleMessage
方法
case LAUNCH_ACTIVITY: {
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
} break;
查看 handleLaunchActivity
方法
Activity a = performLaunchActivity(r, customIntent);
在performLaunchActivity
方法中可以看到
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
这里,我们发现这里通过 Insteumentation
new 了一个 Activity
通过以上代码,我们还可以发现 new 出 Activity 后的几个步骤
- attach Activity, 目测会有初始化 window 的流程
- 设置 theme
- Activity 的
onCreate
流程 - Activity 如果已经销毁,会去执行
onRestoreInstance
,我们可以在这里做数据恢复的操作 - Activity 在
onCreate
完成后的一些操作
到这里,我们的 Activity 就启动成功了
启动新的进程
下面来分析我们的第二种情况,我们可以跟踪到 ActivityManagerService
的 `startProcessLocked 方法, 这个方法最终会走到自己的重载方法:
如果我们启动的是一个 webview service, 则会走到 startWebView
,这里我们不考虑,所以我们分析的是 Process.start
这种初始化一个普通进程的情况。
这个方法最后调用了 ZygoteProcess
的 start
方法
这里我们也可以大致分析出来,这里就是在通过 socket 通信请求 Zygote
进程 fork 一个子进程,作为新的 APP 进程,具体流程本篇文章暂时不做深究。
最终我们会启动 ActivityThread
的 main
方法,继续走到 attach
方法
这里我们能看到启动主线程的 Looper, 创建系统 Context 等工作,最终我们走到 ApplicationThread
的 bindApplication
, 代码这里就不贴了,这里负责了 Application 在初始化的时候的各种工作。包括 LoadedAPK
的 makeApplication
过程。
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
这里会发现,正常模式下,我们走到了 ActivityStackSupervisor
的 attachApplicationLocked
方法,后面就又会和第一部分介绍的一样,走到 realStartActivityLocked
方法,去创建并执行 Activity 的生命周期。
总结
到这里,Activity 的启动流程就大致梳理出来了。基本就是,Instrumentation
负责 Activity 的创建和中转, ActivityStackSupervisor
负责 Activity的 栈管理。Activity 都通过了 ActviityServerManager
来进行管理。
大概的关系如下图所示:
后续
这里我只是对Activity的启动流程做了一个简单的梳理。我们会发现每个模块和细节都有几百几百行的代码。完全吃透还得自己下功夫,看源码,尽管这个过程很痛苦。一遍看不懂就再来一遍,跟着博客思路看了不下十遍,努力总会有收获的。
最近收集到一份阿里大神整理的内部资料,包含【Android开发核心知识笔记+2020大厂最新面试题及解析+源码笔记】。如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。
朋友们如果有需要,可以我的【Github】阅读下载.
最后送给大家一句话:行动是老子、知识是儿子、创造是孙子。祝愿大家能找到各自的方法,实现人生的持续突破。
面试必备:Android Activity启动流程源码分析的更多相关文章
- Android Activity启动流程源码全解析(1)
前言 Activity是Android四大组件的老大,我们对它的生命周期方法调用顺序都烂熟于心了,可是这些生命周期方法到底是怎么调用的呢?在启动它的时候会用到startActivty这个方法,但是这个 ...
- Android Activity启动流程源码全解析(2)
接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...
- Activity启动过程源码分析(Android 8.0)
Activity启动过程源码分析 本文来Activity的启动流程,一般我们都是通过startActivity或startActivityForResult来启动目标activity,那么我们就由此出 ...
- Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)
上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...
- Spark(四十九):Spark On YARN启动流程源码分析(一)
引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...
- SpringBoot启动流程源码分析
前言 SpringBoot项目的启动流程是很多面试官面试中高级Java程序员喜欢问的问题.这个问题的答案涉及到了SpringBoot工程中的源码,也许我们之前看过别的大牛写过的有关SpringBoot ...
- springboot的启动流程源码分析
.测试项目,随便一个简单的springboot项目即可: 直接debug调试: 可见,分2步,第一步是创建SpringApplication对象,第二步是调用run方法: 1.SpringApplic ...
- Spring Boot的自动配置原理及启动流程源码分析
概述 Spring Boot 应用目前应该是 Java 中用得最多的框架了吧.其中 Spring Boot 最具特点之一就是自动配置,基于Spring Boot 的自动配置,我们可以很快集成某个模块, ...
- SpringBoot一站式启动流程源码分析
一.前言 由上篇文章我们得知,SpringBoot启动时,就是有很简单的一行代码.那我们可以很清楚的看到这行代码的主角便是SpringApplication了,本文我们就来聊一聊这货,来探寻Sprin ...
随机推荐
- 乘风破浪,Windows11设计和开发指导,全新图标字体和云母材质
Windows11全新的布局设计 Windows 11全新的布局设计已设计为支持现代应用体验.渐进的圆角.嵌套元素和一致的排水沟相结合,营造出柔和.平静.平易近人的效果,强调目的的统一和易用性. ht ...
- 39、wget、curl
39.1.wget介绍: wget命令用来从指定的URL下载文件.wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性,如果是由于网络的原因下载失败, wget会不断的尝试,直到整个文件 ...
- Java:jar包与war包的差异
一般将项目分为两层:服务层和表现层(视图层),通常我们把服务层打包成jar,而把视图层的包打成war包. 仔细对比可以发现: jar包中包含了你写程序的所有服务或者第三方类库,它通常是作为幕后工作者, ...
- 1.3.2、通过Cookie匹配
server: port: 8080 spring: application: name: gateway cloud: gateway: routes: - id: guo-system4 uri: ...
- CG-CTF 签到
int __cdecl sub_401340(unsigned __int8 *a1) { int v2; // [esp+18h] [ebp-D0h] int v3; // [esp+1Ch] [e ...
- HCNA Routing&Switching之动态路由基本概念
前文我们了解了静态路由的相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/14965433.html:今天我们来聊一聊动态路由相关概念: 首先我们要清楚什 ...
- netcore3.1 + vue (前后端分离) ElementUI多文件带参数上传
vue前端代码 前端主要使用了ElementUI的el-uploda插件,除去业务代码需要注意的是使用formdata存储片上传时所需的参数 <el-upload class="upl ...
- git时 Failed to connect to 127.0.0.1 port 1080: Connection refused
在公司换了一台电脑之后发现git clone 和 npm install都失败,报错为 fatal: unable to access 'https://github.com/netease-im/N ...
- c语言:DEV-C++5.10调试设置
DEV-C++调试设置方法:默认不能调试,打开调试的方法: 1.点击"工具"菜单--编译选项--"代码生成/优化"--连接器--"产生调试信息&quo ...
- 无需kubectl!快速使用Prometheus监控Etcd
在本文中,我们将安装一个Etcd集群并使用Prometheus和Grafana配置监控,以上这些操作我们都通过Rancher进行. 我们将看到在不需要依赖的情况下充分利用Rancher的应用商店实现这 ...