Android应用进程与系统进程的通信

framework

Posted by Cc1over on March 29, 2019

Android应用进程与系统进程的通信

引言

网上关于Activity的启动流程以及各种Binder IPC进程通信的文章数不胜数,大多都是从应用进程到系统进程,又从系统进程回到应用进程,笔者在看这些文章的时候,其实有时真的会很烦,因为整篇文章都各种方法填充,所以笔者看了一些这样的文章之后,决心写一篇自己看起来舒服点的文章,意在总结和分析一些系统的流程和设计用意

进程通信接口的实现

应用进程需要频繁与系统进程通信,譬如Activity生命周期的各个方法都是需要经过系统进程调度的,只是在应用进程进行回调,这就需要从系统到应用的跨进程调用; 应用进程有需要将当前Activity的状态告诉系统进程,以便系统将Activity驱动到下一个状态,这就需要从应用到系统的跨进程调用。

而在Android中实现进程通信的方式笔者了解最多就是Binder了,而四大组件的启动以及状态的维护,其实都是通过Binder去实现进程通信,而Binder的底层细节以及原理则是不是本篇文章的重点,本篇文章的重点是基于Binder之上的通信流程

  • IApplicationThread: 作为系统进程请求应用进程的接口
  • IActivityManager: 作为应用进程请求系统进程的接口

应用进程与系统进程的通信模型:

IActivityManager以及IApplicationThread这个Binder类的类族图如下:

白色表示运行在系统进程,灰色表示运行在应用进程

在系统进程一侧

  • 最上层的有IActivityManager的接口定义
  • 往下一层,有两个接口的实现,其中一个ActivityManagerNative作为Stub/服务端,其主要职责就是对远程传递过来的数据进行反序列化,另一个ActivityManagerProxy作为服务的代理运行在客户端,其主要职责就是将数据进行序列化,再传递给远程的服务端
  • 再下一层,就是接口的具体业务实现AMS了,对Stub做了继承扩展
  • ProcessRecord类和ActivityRecord类的对象是运行在系统进程之中,它们都由AMS管理
  • AMS通过ActivityRecord来维护Activity运行时的状态信息

在应用进程一侧

  • 最上层的有IApplicationThread的接口定义,
  • 往下一层,同样有代理和Stub
  • 再下一层,具体业务的实现类是ApplicationThreadActivityThread的一个内部类,ApplicationThread负责响应系统进程发起的请求,这些请求大部分都是需要调度在应用进程的主线程执行,而ActivityThread是应用进程的主线程,通过Handle往主线程抛消息,ApplicationThread就轻松将具体执行任务的工作转交给了主线程。

  • ActivityActivityThread运行在应用进程,将Activity绑定到AMS中的ActivityRecord就能开始Activity的生命周期

从通信绑定角度看Activity启动过程

Activity的启动过程什么复杂,设计的方法调用以及逻辑很多,但是从不同的视角去看Activity启动过程就可以关注到不一样的细节

当启动一个全新的Activity时,Activity的宿主进程可能还未启动,这时候就需要先启动宿主进程

  • AMS通过ProcessRecord来维护进程运行时的状态信息,需要将应用进程绑定到ProcessRecord才能开始一个Application的构建

  • AMS通过ProcessRecord来维护Activity运行时的状态信息,需要将Activity绑定到AMS中的ActivityRecord能开始Activity的生命周期

Application与ProcessRecord的绑定

从应用进程到系统进程

在ActivityThread创建的时候,会将自己的ApplicationThread绑定到AMS中,调用关系如下所示:

ActivityThread.main()
└── ActivityThread.attach()
    └── IActivityManager.attachApplication(mAppThread)
        └── Binder.transact()

应用进程作为客户端,通过IAcitivtyManager接口发起了跨进程调用, 跨进程传递的参数mAppThread就是IApplicationThread**的实例, 执行流程从应用进程进入到系统进程:

ActivityManagerService.onTransact()
└── ActivityManagerService.attachApplication(IApplicationThread thread)

AMS作为IActivityManager接口的服务端实现,会响应客户端的请求,最终AMS.attachApplication()函数会被执行, 该函数接收跨进程传递过来的IApplicationThread实例,将其绑定到系统进程。 具体的绑定操作细节此处不表,我们只需要知道AMS中维护了所有进程运行时的信息(ProcessRecord),一旦发生了应用进程的绑定请求, ProcessRecord.thread就被赋值成应用进程的IApplicationThread实例,这样一来,在AMS中就能通过该实例发起向应用进程的调用

AMS对于ApplicationThread的管理

 final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();

AMS里面用的是Android中的数据结构SparseArray对ProcessRecord进行存储,key是pid,value则是ProcessRecord

从应用进程到系统进程

AMS.attachApplication()的过程中,会有一些信息要传递给应用进程,以便应用进程的初始化,系统进程会发起如下函数调用:

ActivityManagerService.attachApplication()
└── ActivityManagerService.attachApplicationLocked()
    └── IApplicationThread.bindApplication(processName, appInfo ...)
        └── Binder.transact()

此时,AMS会反转角色,即系统进程作为客户端,通过IApplicationThread接口向应用进程发起调用。 AMS中维护了ProcessRecord这个数据结构,包含了进程运行时的信息,譬如应用进程的名称processName、解析AndroidManifest.xml得到的数据结构ApplicationInfo等,其中,要传递给应用进程的数据都是Parcelable类型的实例。应用进程响应请求的调用关系如下所示:

ApplicationThread.onTransact()
└── ApplicationThread.bindApplication()
    └── ActivityThread.H.handleMessage(BIND_APPLICATION)
        └── ActivityThread.handleBindApplication()
            └── Application.onCreate()

ApplicationThread作为IApplicationThread接口的服务端实现,运行在应用进程中, 然后ApplicationThread.bindApplication()会被执行,完成一些简单的数据封装(AppBindData)后,通过Handler抛出BIND_APPLICATION消息。这一抛,就抛到了主线程上,ActivityThread.handleBindApplication()会被执行,接着就到了各位观众较为熟悉的Application.onCreate()函数。历经应用进程和系统进程之间的一个来回,总算是创建了一个应用程序

Acitivity与ActivityRecord的绑定

在Activity类中有一个IBinder类型的属性:

private IBinder mToken;

IBinder类型表示这个属性是一个远程对象的引用,取了一个恰如其分的变量名:mToken。 为什么叫Token呢?这个名字源自于IApplicationToken.aidl这个接口, 最终ActivityRecord中的一个内部类Token实现了这个接口:

static class Token extends IApplicationToken.Stub {
    final WeakReference<ActivityRecord> weakActivity;

    Token(ActivityRecord activity) {
        weakActivity = new WeakReference<ActivityRecord>(activity);
    }
    ...
}

Token持有了一个ActivityRecord实例的弱引用。在创建一个ActivityRecord的时候,就会创建了一个Token类型的对象:

ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
    int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
    ActivityInfo aInfo, Configuration _configuration,
    ActivityRecord _resultTo, String _resultWho, int _reqCode,
    boolean _componentSpecified, ActivityStackSupervisor supervisor,
    ActivityContainer container, Bundle options) {
    service = _service
    appToken = new Token(this);
    ...
}

构造一个ActivityRecord时,会将自己(this)传给Token,变量ActivityRecord.appToken存的就是最终创建出来的Token

将Token传递给Activity

在启动一个新的Activity时,AMS会将ActivityRecord的Token传递给应用进程,调用关系如下所示:

ActivityStackSupervisor.realStartActivityLocked(ActivityRecord, ...)
└── IApplicationThread.scheduleLaunchActivity(...token, ...)
    // 将ActivityRecord的Token跨进程传递给应用进程
    └── Binder.transact()

ActivityStackSupervisor.realStartActivityLocked()表示要启动一个Activity实例,ActivityRecord作为参数。从ActivityRecord中提取出Token对象,作为跨进程调用的参数,通过IApplicationThread.scheduleLaunchActivity()传递到应用进程

应用进程这一侧,会收到启动Activity的跨进程调用,触发以下一系列的函数调用:

ApplicationThread.onTransact()
└── ApplicationThread.scheduleLaunchActivity(...token, ...)
    // token将被封装进ActivityClientRecord这个数据结构中
    └── ActivityThread.H.handleMessage()
        └── ActivityThread.handleLaunchActivity(LAUNCH_ACTIVITY)
            └── ActivityThread.performLaunchActivity(ActivityClientRecord, ...)
                // 从ActivityRecord取出token
                └── Activity.attch(...token, ...)

标准的Binder服务端处理流程,收到AMS传递过来的Token对象,进行一下数据封装(ActivityClientRecord),然后通过Handler抛出一个LAUNCH_ACTIVITY消息。这个消息显然也是抛到了应用进程的主线程去执行,所以ActivityThread.performLaunchActivity()函数会在主线程上执行,该函数从封装的数据结构ActivityClientRecord中取出Token对象,调用Activity.attach()函数,将其绑定到Activity上,如此一来,就建立应用进程的Activity与系统进程中ActivityRecord的关联。

系统进程维护的是ActivityRecord,应用进程维护的是Activity,两者之间的映射关系就是利用Token来维系的。 应用进程的Activity在创建的时候,就被赋予了一个Token,拿着这个Token就能完成后续与系统进程的通信。 在发生Activity切换时,应用进程会将上一个Activity的Token(AMS.startActivity()的输入参数resultTo)传递给系统进程,系统进程会根据这个Token找到ActivityRecord,对其完成调度后,再通知应用进程:Activity状态发生了变化。

参考资料:

http://duanqz.github.io/2016-01-29-Activity-IPC#22-acitivity%E4%B8%8Eactivityrecord%E7%9A%84%E7%BB%91%E5%AE%9A