源码分析——从AIDL的使用开始理解Binder进程间通信的流程

Binder通信是Android系统架构的基础。本文尝试从AIDL的使用开始理解系统的Binder通信。

0x00 一个AIDL的例子

首先我们创建一个项目,写一个RemoteService.java,并定义个AIDL接口IRemoteService.aidl

  1. interface IRemoteService {
  2. String getText();
  3. }

这时候IDE会自动在目录build/generated/source/aidl/debug/生成IRemoteService.java文件。

本文为了方便调试和理解AIDL的过程,我们把生成的IRemoteService.java文件拷贝出来,放在app/main/java目录下,然后把aidl文件夹删除。

RemoteService为服务端,MainActivity为客户端。最后项目结构为

0x01 远程服务RemoteService

  1. public class RemoteService extends Service {
  2. public final static String ACTION = "net.angrycode.RemoteService";
  3. @Nullable
  4. @Override
  5. public IBinder onBind(Intent intent) {
  6. return mBinder;
  7. }
  8. /**
  9. * 定义远程服务对外接口
  10. */
  11. IRemoteService.Stub mBinder = new IRemoteService.Stub() {
  12. @Override
  13. public String getText() throws RemoteException {
  14. return "text from remote,pid:" + Process.myPid();
  15. }
  16. };
  17. }

RemoteService中定义IBinder接口,并在onBind()方法中返回,供客户端使用。

最后在mainifest文件中注册远程服务,指定进程为私有进程

  1. <service android:name=".RemoteService"
  2. android:process=":remote">
  3. <intent-filter>
  4. <action android:name="net.angrycode.RemoteService"/>
  5. </intent-filter>
  6. </service>

0x02 本地客户端MainActivity

  1. public class MainActivity extends AppCompatActivity {
  2. private TextView mTextMessage;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. mTextMessage = (TextView) findViewById(R.id.message);
  8. }
  9. public void onClickBind(View view) {
  10. Intent service = new Intent(this, RemoteService.class);
  11. service.setAction(RemoteService.ACTION);
  12. bindService(service, conn, Context.BIND_AUTO_CREATE);
  13. }
  14. public void onClickUnBind(View view) {
  15. unbindService(conn);
  16. }
  17. ServiceConnection conn = new ServiceConnection() {
  18. @Override
  19. public void onServiceConnected(ComponentName name, IBinder service) {
  20. IRemoteService iRemoteService = IRemoteService.Stub.asInterface(service);
  21. try {//连接之后获取到远程服务text
  22. String text = iRemoteService.getText();
  23. mTextMessage.setText(text);
  24. } catch (RemoteException e) {
  25. e.printStackTrace();
  26. }
  27. Toast.makeText(getApplication(), "远程服务已连接", Toast.LENGTH_LONG).show();
  28. }
  29. @Override
  30. public void onServiceDisconnected(ComponentName name) {
  31. Toast.makeText(getApplication(), "远程服务已断开", Toast.LENGTH_LONG).show();
  32. }
  33. };
  34. }

本地客户端实现了ServiceConnection接口,用于监听远程服务的连接状态,并在onServiceConnected()中拿到远程服务RemoteService对外的接口IRemoteService的引用。

当客户端进行绑定远程服务时,就使用IRemoteService.Stub.asInterface(IBinder)获取到远程服务对象,客户端与服务端的通信就开始了。

0x03 IRemoteService接口

系统自动生成的这个文件中有除了我们定义getText()方法外还生成了两个内部类StubProxy

  1. public interface IRemoteService extends android.os.IInterface {
  2. /**
  3. * Local-side IPC implementation stub class.
  4. */
  5. public static abstract class Stub extends Binder implements IRemoteService {
  6. private static final java.lang.String DESCRIPTOR = "net.angrycode.learnpro.IRemoteService";
  7. /**
  8. * Construct the stub at attach it to the interface.
  9. */
  10. public Stub() {
  11. this.attachInterface(this, DESCRIPTOR);
  12. }
  13. /**
  14. * Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
  15. * generating a proxy if needed.
  16. */
  17. public static IRemoteService asInterface(IBinder obj) {
  18. if ((obj == null)) {
  19. return null;
  20. }
  21. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  22. if (((iin != null) && (iin instanceof IRemoteService))) {
  23. return ((IRemoteService) iin);
  24. }
  25. return new IRemoteService.Stub.Proxy(obj);
  26. }
  27. @Override
  28. public IBinder asBinder() {
  29. return this;
  30. }
  31. @Override
  32. public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
  33. switch (code) {
  34. case INTERFACE_TRANSACTION: {
  35. reply.writeString(DESCRIPTOR);
  36. return true;
  37. }
  38. case TRANSACTION_getText: {
  39. data.enforceInterface(DESCRIPTOR);
  40. java.lang.String _result = this.getText();
  41. reply.writeNoException();
  42. reply.writeString(_result);
  43. return true;
  44. }
  45. }
  46. return super.onTransact(code, data, reply, flags);
  47. }
  48. private static class Proxy implements IRemoteService {
  49. private IBinder mRemote;
  50. Proxy(android.os.IBinder remote) {
  51. mRemote = remote;
  52. }
  53. @Override
  54. public android.os.IBinder asBinder() {
  55. return mRemote;
  56. }
  57. public java.lang.String getInterfaceDescriptor() {
  58. return DESCRIPTOR;
  59. }
  60. @Override
  61. public java.lang.String getText() throws RemoteException {
  62. android.os.Parcel _data = android.os.Parcel.obtain();
  63. android.os.Parcel _reply = android.os.Parcel.obtain();
  64. java.lang.String _result;
  65. try {
  66. _data.writeInterfaceToken(DESCRIPTOR);
  67. mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
  68. _reply.readException();
  69. _result = _reply.readString();
  70. } finally {
  71. _reply.recycle();
  72. _data.recycle();
  73. }
  74. return _result;
  75. }
  76. }
  77. static final int TRANSACTION_getText = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  78. }
  79. public java.lang.String getText() throws android.os.RemoteException;
  80. }

Stub类继承于Binder,但它们都实现了IRemoteService接口。

Binder是何物呢?

Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by Binder.This class is an implementation of IBinder that provides standard local implementation of such an object.

可以看出Binder是一个远程对象,它实现了提供本地标准接口的IBinder

Stub类代表着远程服务,而Proxy代表着远程服务在本地的代理。

0x04 获取Binder对象

在客户端MainActivity中,绑定远程服务之后,使用IRemoteService.Stub.asInterface()方法获取到远程服务的Binder对象。

  1. /**
  2. * Cast an IBinder object into an net.angrycode.learnpro.IRemoteService interface,
  3. * generating a proxy if needed.
  4. */
  5. public static net.angrycode.learnpro.IRemoteService asInterface(android.os.IBinder obj) {
  6. if ((obj == null)) {
  7. return null;
  8. }
  9. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  10. if (((iin != null) && (iin instanceof net.angrycode.learnpro.IRemoteService))) {
  11. return ((net.angrycode.learnpro.IRemoteService) iin);
  12. }
  13. return new net.angrycode.learnpro.IRemoteService.Stub.Proxy(obj);
  14. }

这个方法先查找本地是否存在这个对象,存在则返回;不存在则返回一个Proxy对象。

通过定点调试,可以知道当RemoteService在子进程中时,asInterface(obj)参数是一个BinderProxy对象,这个是远程服务进程的代理类。这个时候返回给客户端的是Proxy对象。

客户端与服务端不在同一进程时,通过BinderProxy进行通信

当把manifestRemoteServiceandroid:process=':remote'配置去掉时,asInterface(obj)的参数的传递就是RemoteService$1,其实就是RemoteService里面的内部类Stub

然后我们再回到多进程的流程来,跳转到Proxy

0x05 Proxy.transact()

通过名字知道Proxy就是远程服务的代理,它持有Binder的引用。当客户端调用iRemoteService.getText()时其实是进入到Proxy类中getText()方法。

  1. public java.lang.String getText() throws android.os.RemoteException {
  2. android.os.Parcel _data = android.os.Parcel.obtain();
  3. android.os.Parcel _reply = android.os.Parcel.obtain();
  4. java.lang.String _result;
  5. try {
  6. _data.writeInterfaceToken(DESCRIPTOR);
  7. mRemote.transact(Stub.TRANSACTION_getText, _data, _reply, 0);
  8. _reply.readException();
  9. _result = _reply.readString();
  10. } finally {
  11. _reply.recycle();
  12. _data.recycle();
  13. }
  14. return _result;
  15. }

首先获取到两个Parcel对象,这个是进程间通信的数据结构。_data_reply分别为getText()需要传递的参数和返回值,getText()无需参数,只有String类型返回值。

然后调用mRemotetransact()方法(其实就是调用BinderProxytransact()方法)。然后通过_reply获取到执行方法后的返回值,这里就是一个RemoteService里面实现的String

Proxy中执行transact()方法后又回调到哪里了呢?

onTransact()方法中设置一个断点,通过调试,我们发现其实是回调到了Stub类中onTransact()方法

0x06 Stub.onTransact()

  1. public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
  2. switch (code) {
  3. case INTERFACE_TRANSACTION: {
  4. reply.writeString(DESCRIPTOR);
  5. return true;
  6. }
  7. case TRANSACTION_getText: {
  8. data.enforceInterface(DESCRIPTOR);
  9. java.lang.String _result = this.getText();
  10. reply.writeNoException();
  11. reply.writeString(_result);
  12. return true;
  13. }
  14. }
  15. return super.onTransact(code, data, reply, flags);
  16. }

onTransact()方法中第一个参数code是与transact()第一个参数code是对应的,这是客户端与服务端约定好的常量。

这时候会执行到onTransact()方法中的_result = this.getText()方法。而Stub类是在RemoteService中实现的,故就访问到远程服务中资源了。

0x07 总结

通过以上流程分析可以知道,通过bindService绑定一个服务之后在onServiceConnected()中拿到了远程服务的在本地的Proxy,通过它与远程服务进行通信。

  1. 微信关注我们,可以获取更多

源码分析——从AIDL的使用开始理解Binder进程间通信的流程的更多相关文章

  1. [源码分析] 带你梳理 Flink SQL / Table API内部执行流程

    [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 目录 [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 0x00 摘要 0x01 Apac ...

  2. ThreadPoolExecutor源码分析-面试问烂了的Java线程池执行流程,如果要问你具体的执行细节,你还会吗?

    Java版本:8u261. 对于Java中的线程池,面试问的最多的就是线程池中各个参数的含义,又或者是线程池执行的流程,彷佛这已成为了固定的模式与套路.但是假如我是面试官,现在我想问一些更细致的问题, ...

  3. JDK源码分析之hashmap就这么简单理解

    一.HashMap概述 HashMap是基于哈希表的Map接口实现,此实现提供所有可选的映射操作,并允许使用null值和null键.HashMap与HashTable的作用大致相同,但是它不是线程安全 ...

  4. JVM 源码分析(三):深入理解 CAS

    前言 什么是 CAS Java 中的 CAS JVM 中的 CAS 前言 在上一篇文章中,我们完成了源码的编译和调试环境的搭建. 鉴于 CAS 的实现原理比较简单, 然而很多人对它不够了解,所以本篇将 ...

  5. JVM 源码分析(四):深入理解 park / unpark

    前言 Parker 源码调试与分析 park/unpark 原理总结 补充:jstack 命令和 kill 命令 前言 熟悉 Java 并发包的人一定对 LockSupport 的 park/unpa ...

  6. sizzle源码分析 (3)sizzle 不能快速匹配时 选择器流程

    如果快速匹配不成功,则会进入sizzle自己的解析顺序,主要流程如下: 总结流程如下: (1)函数sizzle是sizzle的入口,如果能querySelectAll快速匹配,则返回结果 (2)函数S ...

  7. 内核通信之Netlink源码分析-用户内核通信原理

    2017-07-05 本节从一个小案例入手,结合源码分析下通过netlink进行内核和用户通信的流程. 内核端 按照传统CS模式,其实内核端可以作为是服务器端,用以接收用户的请求并作出处理,但是从ne ...

  8. mybatis 源码分析(二)mapper 初始化

    mybatis 的初始化还是相对比较复杂,但是作者在初始化过程中使用了多种设计模式,包括建造者.动态代理.策略.外观等,使得代码的逻辑仍然非常清晰,这一点非常值得我们学习: 一.mapper 初始化主 ...

  9. 还不懂 ConcurrentHashMap ?这份源码分析了解一下

    上一篇文章介绍了 HashMap 源码,反响不错,也有很多同学发表了自己的观点,这次又来了,这次是 ConcurrentHashMap 了,作为线程安全的HashMap ,它的使用频率也是很高.那么它 ...

随机推荐

  1. <C++Primer>第四版 阅读笔记 第二部分 “容器和算法”

    泛型算法中,所谓"泛型(generic)"指的是两个方面:这些算法可作用于各种不同的容器类型,而这些容器又可以容纳多种不同类型的元素. 第九章 顺序容器 顺序容器的元素排列次序与元 ...

  2. 调用win32 api 函数SendMessage() 实现消息直接调用

    简单的调用例子, 适合初学者学习,当然 我也是初学者. #include <windows.h> #include <stdio.h> #include <stdlib. ...

  3. uml系列(八)——部署图与构件图

    之前说了uml的设计图,现在说一下uml的最后两种图:构件图.部署图.这两种图之所以放在一起是因为它们都是软件的实现图. 构件图           构件图是描述一组构件之间的组织与依赖关系的模型.那 ...

  4. PLSQL程序流程

    IF语句结构: if(条件表达式)- -then- -执行语句;- -end; IF-THEN-ELSE语句结构: if(条件表达式)- -then- -执行语句;- -else- -执行语句;- e ...

  5. matlab 利用while循环计算平均值和方差

    一.该程序是用来测输入数据的平均值和方差的 公式: 二. 项目流程: 1. State the problem假定所有测量数为正数或者0,计算这一系列测量数的平均值和方差.假定我们预先不知道有多少测量 ...

  6. 使用光盘iso实现Linux操作系统的自动安装部署

    前边写了一篇使用 PXE 的方式批量安装操作系统,不是任何时候任何地方都有环境来通过 PXE 方式来进行安装.如果此时需要通过光盘安装,默认的情况下是通过交互式方式进行安装,其实也可以通过 kicks ...

  7. phpcmsV9静态页面替换动态步骤

    1.先在www目录下找到 phpcms + install_package + phpcms + templates在templates 文件夹里创建个自己的文件夹我弄得是 ceshi 文件夹,在 c ...

  8. javascript组件的基本结构

    (function(window, undefined) { function JsClassName(cfg) { var config = cfg || {}; this.get = functi ...

  9. web请求

    概述 发起一个http请求的过程就是建立一个socket通信的过程. 我们可以模仿浏览器发起http请求,譬如用httpclient工具包,curl命令等方式. curl "http://w ...

  10. video.js支持m3u8格式直播

    为什么要使用video.js? 1. PC端浏览器并不支持video直接播放m3u8格式的视频 2. 手机端各式各样的浏览器定制的video界面风格不统一,直接写原生的js控制视频兼容性较差 3. v ...