AMS常用知识学习 1 app与ams通信

前言

其实在网上已经有数不尽的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查看到这个进程,如下图:
AMS常用知识学习 1 app与ams通信
我们当然还可以通过ps -t -p system_server进程id去查看对应的线程信息.(高版本模拟器请用ps -T system_server进程id)

AMS常用知识学习 1 app与ams通信
你可以看到system_server这个进程里面其实包含了很多其他服务所以当你想调试AMS的java代码只需选中这个进程即可(AMS是java代码所以自然运行到虚拟机里面,而android虚拟机也实现了JDWP,所以我们调试的原理也是这个,你可以看到上面的线程信息中存在一个JDWP线程).

AMS和我们所在开发App是在不同的进程.而AMS掌控的四大组件的生命的调度与创建.如下举例一个打开一个界面:
AMS常用知识学习 1 app与ams通信
我们知道AppAMS处于不同进程,所以之间需要通过Binder通信.

  • App给AMS发送信息

App可以通过ServiceManager查询到AMS,从而获取到AMS对应的接口代理类,通过这个AMS接口代理类我们可以像AMS发送各种消息.这个代理接口是IActivityManager.

  • AMSApp发送消息
    假设用户关闭了某个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常用知识学习 1 app与ams通信

  • AMS调用App函数

AMS常用知识学习 1 app与ams通信

上一篇:Android插件化主流框架和实现原理,全网独家首发!


下一篇:Android插件化主流框架和实现原理,全网独家首发!