前言
其实在网上已经有数不尽的Activity
启动流程分析,但是我很多时候喜欢自己在去跟着源码看一遍.加上今年花了很长时间学习Binder
所以在次看源码的时候发现思考的方向也有所不同.
本文讲解的源码版本为Android 5.0
,不会像其他文章一样深入
本文所需的前置知识:
Binder驱动源码分析目录
AMS 和App的关系
AMS基础知识
AMS
全称为ActivityManagerService
,在AMS
启动时会向ServiceManager
注册服务,服务名为activity
(binder基础知识可参考博主).既然像ServiceManager
注册过服务那么开发者便可以通过名称获取,如下代码所示:
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
//获取ams服务 public static final String ACTIVITY_SERVICE = "activity";
val activityService = this.getSystemService(Context.ACTIVITY_SERVICE)
}
}
AMS
并不是一个单独的进程而是一个运行在system_server
进程中的一个线程.
您可用ps | grep system_server
查看到这个进程,如下图:
我们当然还可以通过ps -t -p system_server进程id
去查看对应的线程信息.(高版本模拟器请用ps -T system_server进程id
)
你可以看到system_server
这个进程里面其实包含了很多其他服务所以当你想调试AMS
的java代码只需选中这个进程即可(AMS是java代码所以自然运行到虚拟机里面,而android虚拟机也实现了JDWP
,所以我们调试的原理也是这个,你可以看到上面的线程信息中存在一个JDWP线程).
AMS
和我们所在开发App
是在不同的进程.而AMS
掌控的四大组件的生命的调度与创建.如下举例一个打开一个界面:
我们知道App
和AMS
处于不同进程,所以之间需要通过Binder
通信.
- App给AMS发送信息
App
可以通过ServiceManager
查询到AMS
,从而获取到AMS
对应的接口代理类
,通过这个AMS接口代理类
我们可以像AMS
发送各种消息.这个代理接口是IActivityManager
.
-
AMS
给App
发送消息
假设用户关闭了某个App
的界面,AMS
有必要告诉App
这个Activity
应该被销毁.但是如何回调呢?App可没有像ServiceManager
注册过服务.这个问题读者问了在大厂的一些同学貌似也没能回答上.
解决方案:
让App进程创建一个IBinder的服务端,即App有一个类实现了IBinder
接口,在App
获取ASM
代理接口类时,通过代理接口类写入这个IBinder
对象即可.这样AMS便可以通过这个类跟App交互.
下面是一个小的Demo
并非官方代码,不过对于理解AMS
交互很有帮助
class MyApplicationThread : Binder() {
//注意onTransact回调到binder线程池
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean {
//AMS发来一个code表示启动一个activity
if (code == 20) {
if (data != null) {
//获取这个activity名字
val activityName = data.readString()
//启动
launcherActivity(activityName)
return true
}
}
return super.onTransact(code, data, reply, flags)
}
//启动activity
fun launcherActivity(activityName: String) {
//略
}
}
//通过AMS代理接口写入
class MyData() : Parcelable {
var ipcObj: MyApplicationThread? = null;
constructor(parcel: Parcel) : this() {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeStrongBinder(ipcObj)
}
}
//xxx函数在app启动时调用
fun xxx(){
//获取AMS代理接口
val ams:IActivityManager=//略
//构造一个对象准备传给ams,这里我们甚至可以不通过Parcelable类传输.
var myData=MyData();
//赋值好用于交互的远程对象
myData.ipcObj=//略
//attachApplication是ams预定义的函数,用于告诉ams你的远程调用类,后面有事件我会通过他驱动
ams.attachApplication(myData)
}
上面便是AMS
怎么与App
通信的伪代码,我们接下来分析下实际这块相关源码
我们知道App启动时会调用ActivityThread.main
函数
class ActivityThread{
public static void main(String[] args) {
//loop相关代码这里就不将可
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
//关联ams
thread.attach(false);
//启动loop循环
Looper.loop();
}
//一个实现了IBinder了IBinder远程调用类
final ApplicationThread mAppThread = new ApplicationThread();
private void attach(boolean system) {
if (!system) {
//获取ams代理接口,通过ServiceManager的名字查询到ams.
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//将一个Ibinder对象写入到AMS
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
}
}
}
tip:上面在写入一个
Binder
对象给AMS
时,会在Binder驱动的app进程的上下文对象的binder红黑树中多挂载一个与之对应的对象.如果你有兴趣可以参考博主binder源码分析系列.
其实在腾讯开源的插件化框架shadow中你也看到类似的代码.
我们总结AMS的交互方式下:
App调用调用ASM进行交互
- AMS调用App函数