趁着周末最后一天的晚上,大概看了startActivity 的总体的流程,目前还非常的不完善,记录下。
看源码是想要回答以及更好的理解下面6个问题。
- AMS 是用来做什么的。
- Activity 启动的完整流程,如何去寻找需要跳转的Activity 的。
- 权限问题是怎么解决的。
- Activity 中的token 是什么。
- 具体的task 是怎么工作的。
- 知道了AMS 启动Activity 的流程以后可以做什么。
主要涉及到的类:
android.app.*
Activity
Instrumentation
ActivityThread
ApplicationThread
ActivityManagerNative
ActivityManagerProxy
ApplicationThreadNative
ApplicationThreadProxycom.android.server.am.*
ActivityManagerService
ActivityRecord
ActivityStarter
ActivityStack
ActivityStackSupervisor
startActivity 流程分析
最开始这个地方分析的比较混乱,所以重新总结了一下(一些疑惑直接写在了时序图里面)。主要是将startActivity 的过程看成了3个过程:调用端pause 过程、被调用端launch 过程、调用端stop 过程。这个过程也是在应用开发中的流程,比如调用端执行onPause 以后才会执行被调用端的onCreate。
里面的分析流程是在同一个应用中打开的流程,所以没有涉及到Application 的创建的过程。
总体涉及到的进程间通信
总体涉及到了两次IPC 通信。
调用端pause 过程

12 里面有一个Binder 进程间通信的知识点。
|
|
Binder进程间通信是阻塞的,但是IBinder.FLAG_ONEWAY可以实现非阻塞,意思是调用scheduleStopActivity以后可以直接执行后面的代码而不用等待进程间通信返回的结果。
被调用端launch 过程

17performLaunchActivity里,涉及到了Activity的创建完整过程。(注意这里因为是同一个Application的startActivity所以没有涉及到Application#onCreate)
调用端stop 过程

这里Idler涉及到了MessageQueue.IdleHandler的一个机制,代表的是在MessageQueue中还有消息需要执行,但是当前时间点没有消息,会执行IdleHandler的消息(相当于是在空闲的时间点执行消息)。
查找Activity 具体流程
|
|
最终AppGlobals.getPackageManager()执行ActivityThread.getPackageManager,得到的是PackageManagerService。
|
|
主要是看3和4处的代码,queryIntentActivitiesInternal看名称是通过Intent查找满足条件的所有Activity,而chooseBestActivity则是从所有满足条件的Activity中选择最合适的一个。
queryIntentActivitiesInternal
首先看queryIntentActivitiesInternal,如下。
|
|
简化了很多代码,主要分成两种情况,如果已经设置了ComponentName(已经知道了包名和打开的Activity),可以直接找到。看第7行代码就行。
|
|
直接看第5行代码,通过ComponentName在ArrayMap找到相应的相应的Activity。
总结:mActivities这个对象保存在PackageManagerService中,里面存储了系统中所有的Activity(键值对保存着ComponentName的情况下寻找Activity直接使用map.get。
回到上面的queryIntentActivitiesInternal,在comp == null的情况,首先看到27行,里面直接调用了IntentResolver.queryIntent。在里面会根据resolvedType(与设置的数据有关)、scheme、action、catrgories来处理。在看到34行,通过mPackages已经过滤的数据一次的数据来处理的。
总结:在ComponentName不为null 的情况下,使用ActivityIntentResolver里面已经保存的数据(例如:mActionToFilter 保存了相应Action 对应的所有的数据Activity 的信息)来查询满足的数据。
chooseBestActivity
选择最合适的Activity过程,通过上面的步骤查到了所有满足的Activity,但实际上一次只能打开一个Activity,所以需要去判断,这里已经把分析写在了注释。
|
|
总结:通过Intent查询最终打开的Activity的过程是通过PackageManagerService来完成的。PackageManagerService中的mPackages、mActivities分别表示了系统中Package以及Activity的数据(想要非常清楚的明白这些值是怎么来的还需要去看PackageManagerService的工作原理)。还有一个应用开发的问题是在自定义intent-filter的时候最好是加上<category android:name="android.intent.category.DEFAULT"/>。
权限问题
ActivityNotFoundException这个比较容易出现的错误是在Instrumentation#checkStartActivityResult。没有在AndroidManifest中声明Activity。
然后就是权限错误了。ActivityStackSupervisor#checkStartAnyActivityPermission有具体的判断逻辑。主要记录下有两点需要注意的地方。
android:exported="true"可以让其他app 打开自己的Activity。- 如果有必要可以自己声明权限。
在App1 中声明权限egos.app1.zzz,并设置MainActivity3
需要权限。
在App2 中申请权限,那么在App2 中就可以打开App1 的MainActivity3。
|
|
实际的操作中通常设置android:protectionLevel="signature",以及会在两个App 中都声明permission(只在一个App 中声明,可能导致没有声明的App 先安装的话找不到这个权限,没办法注册)。
token 是什么
token 创建的地方
|
|
Token是ActivityRecord的内部类,在里面创建的。
|
|
|
|
所以到这里,token 代表的是Activityecord$Token的对象,也是一个Binder对象。Token中主要保存的是所处的Activityecord信息以及对应的ActivityManagerService的信息。
token 使用的地方
token 到底是什么还是需要看使用的地方,再回到最开始调用Instrumentation#execStartActivity的地方。
|
|
token 被传递进去了。(仔细看上面startActivity的地方其实就已经觉得很奇怪了,调用的时候都不知道到底是哪个Activity发起的startActivity,其实这就是token 的作用)
继续跟进代码,ActivityManagerProxy#startActivity,然后ActivityManagerNative#onTransact,再到ActivityMAnagerService#startActivity。跟着上面的时序图跑,最终在ActivityStarter#startActivityLocked找到了Token 的使用。
|
|
即通过token 拿到了调用端的Activity的相关信息。
总结:正如token 字面意思一样,代表的是Activity的标识。里面记录了Activity的相关信息,在IPC 的时候可以通过Token 找到一个对应的Activity的信息。
任务栈的工作原理
任务栈的用处(个人小总结):
- 回到上一个页面:最基本的back 键回到上一个界面。
- 方便复用:比如singleTask。
- 方便回收内存:内存不够的时候优先回收看不见的
Activity。
可以通过adb shell dumpsys activity activities入手。具体的流程是ActivityManagerService#dumpActivitiesLocked,到ActivityStackSupervisor#dumpActivitiesLocked,再到ActivityStack#dumpActivitiesLocked,再到TaskRecord#dump,最后ActivityRecord#dump。
|
|
根据dump 的文件一层层看下去,Display #代表的是一个ActivityStackSupervisor#ActivityDisplay,Stack #代表的是一个ActivityStack,Task id #代表的是一个TaskRecord,最后Hist #代表的是一个ActivityRecord。这里附上一张网上的图,来源四大组件之ActivityRecord
做应用开发主要是需要能够从这个dump 里面发现自己的问题,所以比较重要的是TaskRecord和ActivityRecord。TaskRecord代表的是常说的任务栈,ActivityRecord则是任务栈中的某个Activity。
最后再看看应用开发中比较特殊的singleTask 和singleInstance。singleTask 代表的是如果在一个TaskRecord里面创建一个ActivityRecord的实例时候发现这个TaskRecord已经有了这个实例那么就会直接使用这个TaskRecord里面的实例,不再创建新的ActivityRecord。singleInstance 则代表的更强的意思,一个singleTask 的实例会单独的保存在一个TaskRecord中。
总结:任务栈的内容在应用开发过程中是一个比较重要的内容,比如app 的主页一般都是需要设置成singleTask 的。
总结
写完以后发现自己分析的还是非常的粗糙(特别是对比网上大牛的博客),但是觉得自己的目的达到了,站在应用开发的角度,去寻找了几个之前没有找到答案的问题。