趁着周末最后一天的晚上,大概看了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 的。
总结
写完以后发现自己分析的还是非常的粗糙(特别是对比网上大牛的博客),但是觉得自己的目的达到了,站在应用开发的角度,去寻找了几个之前没有找到答案的问题。