http://duanqz.github.io/2016-01-29-Activity-IPC

Android四大组件之Activity--应用进程与系统进程的通信


目录

  • 1. 通信接口的实现
  • 2. 启动一个Activity的通信过程
    • 2.1. Application与ProcessRecord的绑定
    • 2.2. Acitivity与ActivityRecord的绑定

请尊重原创版权,转载注明出处。

Android中有一个系统进程(system_process,在Lollipop之前,叫system_server),运行着系统的重要服务(AMS, PMS, WMS),针对Activity而言,系统进程需要不断地调度Activity执行,管理Activity的状态;每一个APK都需要运行在一个应用进程中,有自己独立的内存空间,针对Activity而言,应用进程需要执行Activity生命周期函数(onCreate, onStart, …onDestroy)的具体逻辑。

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

应用进程与系统进程相互通信的手段,就是老生长谈的Binder机制。本文要分析的自然不是Binder机制的内在原理,而是应用进程与系统进程建立在Binder之上通信的业务逻辑,Android为此设计了两个Binder接口:

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

1. 通信接口的实现

先上一张类图,描述了两个Binder接口实现相关的类:

在接口实现手段上,应用进程与系统进程是一致的,毕竟逃不出Binder的标准实现。

在系统进程一侧

  • 最上层的有IActivityManager的接口定义,图中只列出了少数成员函数作为示意;
  • 往下一层,有两个接口的实现,其中一个ActivityManagerNative作为服务端的“桩(Stub)”,其主要职责就是对远程传递过来的数据进行”反序列化(unparcel)”; 另一个ActivityManagerProxy作为服务的“代理(Proxy)”,运行在客户端,其主要职责就是将数据进行“序列化(parcel)”,再传递给远程的“桩(Stub)”;
  • 再下一层,就是接口的具体业务实现AMS了,对“桩(Stub)”做了继承扩展,逻辑庞大到令人害怕。

在应用进程一侧

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

“代理(Proxy)”和“桩(Stub)”是Binder接口实现时成对出现的概念。可以对应到一个生活中的例子:银行的取款机就像是银行的业务代理(Proxy),客户只需要通过取款机,就能方便地完成存、取款等业务。对于客户来说,银行实际是什么样子,钱存在哪,都不重要了,只要有取款机在就够了。对于取款机这个代理而言,它需要连接银行的服务器(Stub),完成数据传递。

上面的类图中,用两种不同的颜色对类进行了区分。除了Binder的标准实现,还示意了几个不同的类依赖关系:

  • ProcessRecord类和ActivityRecord类的对象是运行在系统进程之中,它们都由AMS管理;
  • ProcessRecord依赖于客户端进程中的ApplicationThread对象,而客户端进程中的Activity依赖于ActivityRecord。

这种依赖关系的构建,是在启动一个全新的Activity时,利用IActivityManagerIApplicationThread完成了数据绑定操作。绑定成功以后,相当于应用进程和系统进程相互打了个招呼,知道了彼此的存在,后续就可以进行更加深入友好的交流。

下面,我们就来分析一下启动一个Activity时数据绑定的过程。

2. 启动一个Activity的通信过程

当启动一个全新的Activity时,Activity的宿主进程可能还未启动,这时候就需要先启动宿主进程,在Android四大组件之Activity–管理方式一文中,我们介绍过:

  1. AMS通过ProcessRecord来维护进程运行时的状态信息,需要将应用进程绑定到ProcessRecord才能开始一个Application的构建;
  2. AMS通过ActivityRecord来维护Activity运行时的状态信息,需要将Activity绑定到AMS中的ActivityRecord能开始Activity的生命周期。

这两个绑定操作是应用进程与系统进程相互通信的开始。

2.1. 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.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()函数。历经应用进程和系统进程之间的一个来回,总算是创建了一个应用程序。

2.2. Acitivity与ActivityRecord的绑定

ActivityRecord的Token

在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 = _serviceappToken = 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状态发生了变化。

Token就像一个令牌,揣着这个令牌,就是游走江湖的身份象征。Activity出生的时候,管理者(AMS)就会登记Activity的真实身份(ActivityRecord),并颁发一个令牌(Token)给Activity。以后这个Activity要有什么行为,都要交出令牌,让管理者核实一下真实身份。


  • Android四大组件之Activity--启动过程(下)
  • Android四大组件之Activity--启动过程(上)
  • ActivityManagerService的启动过程
  • Android四大组件之Activity--管理方式
  • Android四大组件之Activity--启动模式

应用进程与系统进程的通信(IActivityManager IApplicationThread)相关推荐

  1. Linux C++服务器项目——网络编程1 (socket通信,服务端,客户端)

    牛客 C++高并发服务器开发 参考笔记 1.MAC地址 2 IP地址 2.1 简介 2.2 IP地址编址方式 2.3 子网掩码 3 端口 3.1 简介 3.2 端口类型 4 网络模型 4.1 OSI七 ...

  2. android AMS

    跟着邓凡平大神的博客走一遍加深理解. AMS是Android中最核心的服务,主要负责系统中四大组件的启动.切换.调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在 ...

  3. Android App启动流程详解

    前言:在之前的文章中已经写了apk的打包流程.安装流程,今天就是梳理一下apk系列的最后的流程--app启动流程.经过今天的梳理以后咱们就可以对apk包是怎么编译生成的.apk是怎么被安装到安卓手机的 ...

  4. android 不能在子线程中更新ui的讨论和分析

    问题描述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是只能在主线程中更改 ui,子线程要修改 ...

  5. Android Applicaion组件创建的源代码分析(Android 9,含序列图)

    Application组件源代码分析 1. Applicaion启动流程源代码分析 2. 启动过程中应用进程.系统服务进程通信的分界点 3. 组件生命周期与系统服务的关系 4. Application ...

  6. 手把手带你搞懂AMS启动原理

    彻底搞懂AMS即ActivityManagerService,看这一篇就够了 前言 最近那么多教学视频(特别是搞车载的)都在讲AMS,可能这也跟要快速启动一个app(甚至是提高安卓系统启动速度有关), ...

  7. [深入理解Android卷二 全文-第六章]深入理解ActivityManagerService

    由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第 ...

  8. Android 深入研究之 ✨ Activity启动流程+Activity生命周期✨

    Activity分析目录 前言 Activity生命周期 1.activity的四个状态 2.activity的生命周期 3.activity优先级 Activity启动流程 Activity的启动流 ...

  9. 从源码解析-结合Activity加载流程深入理解ActivityThrad的工作逻辑

    ActivityThread源码解析 前言 类简称 类简介 一 二 三 四 五 代理和桩的理解 ActivityThread ActivityThread.main AT.attach AMN.get ...

最新文章

  1. [USACO16JAN]Angry Cows S[二分+贪心]
  2. java file pathname_int compareTo(File pathname)
  3. python库怎么绘画_python基础,安装并使用matplotlib库画图
  4. HDFS HA与QJM(Quorum Journal Manager)介绍及官网内容整理
  5. python教材目录,python 目录
  6. 外媒:已有5家芯片厂商获准继续向华为供货
  7. 剑指offer面试题[42]-反转单词顺序VS左旋转字符串
  8. linux删除5天前文件和目录,Linux Shell命令定时删除指定目录下n天前的文件
  9. word的使用(二)
  10. python编一个答题程序_从0到1使用python开发一个半自动答题小程序的实现
  11. VBA代码片之获取行列号
  12. 计算机中的信息计量单位字,计算机中信息的计量单位.pdf
  13. Self-supervised Heterogeneous Graph Neural Network with Co-contrastive Learning
  14. Gnutella 及无结构化(非结构化)P2p的一些总结
  15. GameMakerStudio2调用外部dll库
  16. android可看电视吗,不要VIP也能看电影,安卓手机这4款APP太赞了!
  17. 机器学习数据集-MNIST
  18. 什么是ThreadLocal ?
  19. 基于RNN的短期股票预测
  20. Python爬取中原地产香港26281套在售二手房数据并分析

热门文章

  1. javascript英语单词音节拆分_英语单词音节的划分:成音节详解
  2. android反编译二次打包,Android二次打包简单教程
  3. Android动画之逐帧动画FrameAnimation
  4. 申万一级行业日指数_申万一级行业指数昨日市场表现
  5. Windows 7下的Comodo Firewall免费防火墙
  6. ESP8266-Arduino编程实例-BME680环境传感器驱动
  7. 获得iphone设备的信息
  8. 玩不死你校内农场种地助手
  9. Spring Boot 大型线上商城项目实战教程
  10. 委托代扣问的人很多,我发一下基本要求,劝退一波人吧