Android Binder系列文章:
由浅入深 学习 Android Binder(一)- AIDL
由浅入深 学习 Android Binder(二)- bindService流程
由浅入深 学习 Android Binder(三)- java binder深究(从java到native)
由浅入深 学习 Android Binder(四)- ibinderForJavaObject 与 javaObjectForIBinder
由浅入深 学习 Android Binder(五)- binder如何在进程间传递
由浅入深 学习 Android Binder(六)- IPC 调用流程
由浅入深 学习 Android Binder(七)- IServiceManager与ServiceManagerNative(java层)
由浅入深 学习 Android Binder(八)- IServiceManager与BpServiceManager(native层)
由浅入深 学习 Android Binder(九)- service_manager 与 svclist
由浅入深 学习 Android Binder(十)- 总结
由浅入深 学习 Android Binder(十一) binder线程池

概述

aidl是常用的android IPC方式,本文将根据一个demo来解析下AIDL的原理。

为了便于读者理解,本文不会探究Binder的实现细节,可以认为Binder在此文的分析中被看做是一个“黑盒”。

有一定经验的读者可以直接到文末看总结,最终流程图如下:

Demo

demo实现内容:
MainActivity通过AIDL实现IPC来调用另一个进程中RemoteTestService的一个方法。
主要有以下几个文件:

  • MainActivity.java
    自定义ServiceConnection,然后将ServiceConnection传入bindService,获取到IBinder后实现远程调用。
  • RemoteTestService.java
    在ITestServer.Stub中实现需要远程调用的方法testFunction(),在onBind中返回。
  • IServer.aidl
    定义一个aidl文件和需要远程调用的方法
  • AndroidManifest.xml
    在此设置RemoteTestService在remote进程。

MainActivity.java

public class MainActivity extends AppCompatActivity {private IServer iServer;private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {iServer = IServer.Stub.asInterface(service);System.out.println("onServiceConnected");try {int a = iServer.testFunction("test string");Log.i("test", "after testFunction a:" + a);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i("test", "onServiceDisconnected");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = new Intent(MainActivity.this, RemoteTestService.class);bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);}
}

RemoteTestService.java

public class RemoteTestService extends Service {private IServer.Stub serverBinder = new IServer.Stub() {@Override public int testFunction(String s) throws RemoteException {Log.i("test","testFunction s= " + s);return 0;}};@Nullable @Overridepublic IBinder onBind(Intent intent) {return serverBinder;}
}

IServer.aidl

interface IServer {int testFunction(String s);
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.bindservicetest"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><serviceandroid:name=".RemoteTestService"android:process=":remote" /></application></manifest>

aidl自动生成的文件

定义完aidl文件,编译会自动生成一个java接口文件。
这个接口文件在build目录下,具体路径如下:

打开文件,我们就可以看到aidl自动生成的代码。

public interface IServer extends android.os.IInterface
{/** Default implementation for IServer. */public static class Default implements com.example.bindservicetest.IServer{@Override public int testFunction(java.lang.String s) throws android.os.RemoteException{return 0;}@Overridepublic android.os.IBinder asBinder() {return null;}}/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.example.bindservicetest.IServer{private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.IServer";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.example.bindservicetest.IServer interface,* generating a proxy if needed.*/public static com.example.bindservicetest.IServer asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.bindservicetest.IServer))) {return ((com.example.bindservicetest.IServer)iin);}return new com.example.bindservicetest.IServer.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder(){return this;}@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{java.lang.String descriptor = DESCRIPTOR;switch (code){case INTERFACE_TRANSACTION:{reply.writeString(descriptor);return true;}case TRANSACTION_testFunction:{data.enforceInterface(descriptor);java.lang.String _arg0;_arg0 = data.readString();int _result = this.testFunction(_arg0);reply.writeNoException();reply.writeInt(_result);return true;}default:{return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.example.bindservicetest.IServer{private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote = remote;}@Override public android.os.IBinder asBinder(){return mRemote;}public java.lang.String getInterfaceDescriptor(){return DESCRIPTOR;}@Override public int testFunction(java.lang.String s) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(s);boolean _status = mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);if (!_status && getDefaultImpl() != null) {return getDefaultImpl().testFunction(s);}_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}public static com.example.bindservicetest.IServer sDefaultImpl;}static final int TRANSACTION_testFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);public static boolean setDefaultImpl(com.example.bindservicetest.IServer impl) {if (Stub.Proxy.sDefaultImpl == null && impl != null) {Stub.Proxy.sDefaultImpl = impl;return true;}return false;}public static com.example.bindservicetest.IServer getDefaultImpl() {return Stub.Proxy.sDefaultImpl;}}public int testFunction(java.lang.String s) throws android.os.RemoteException;
}

根据源码的简洁版来解析

aidl文件本身并不会参与代码的运行,编译后自动生成的文件才是关键,因此这个文件由开发者来手动实现也是完全可以的。

自动生成的接口文件,有较多代码在demo的IPC中并没有使用。
因此笔者打算用源码解析就用自己写的接口文件,也是减少给读者的混淆项。
笔者自己实现的 接口ITestServer.java,源码如下:

public interface ITestServer extends android.os.IInterface {public int testFunction(java.lang.String s) throws android.os.RemoteException;public static abstract class Stub extends android.os.Binderimplements com.example.bindservicetest.ITestServer {//binder唯一标识private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.ITestServer";//testFunction的调用idstatic final int TRANSACTION_testFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);public Stub() {//将interface提供出去,这样当统一进程其他位置执行IBinder.queryLocalInterface的时候就可以获取到这个Binderthis.attachInterface(this, DESCRIPTOR);}public static com.example.bindservicetest.ITestServer asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//查找当前进程是否有这个binderif (((iin != null) && (iin instanceof com.example.bindservicetest.IServer))) {return ((com.example.bindservicetest.ITestServer) iin);}return new com.example.bindservicetest.ITestServer.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder() {return this;//继承自IInterface,返回当前Stub的binder对象}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)throws android.os.RemoteException {java.lang.String descriptor = DESCRIPTOR;switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(descriptor);return true;}case TRANSACTION_testFunction: {data.enforceInterface(descriptor);//需要传递DESCRIPTOR,到另一个进程可以找到对应的Binderjava.lang.String _arg0;_arg0 = data.readString();//读取参数int _result = this.testFunction(_arg0);//运行reply.writeNoException();reply.writeInt(_result);//写入返回值return true;}default: {return super.onTransact(code, data, reply, flags);}}}private static class Proxy implements com.example.bindservicetest.ITestServer {private final android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Override public android.os.IBinder asBinder() {return mRemote;//继承自IInterface,返回当前Proxy的binder对象}@Override public int testFunction(java.lang.String s) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);//需要传递DESCRIPTOR,到另一个进程可以找到对应的Binder_data.writeString(s);//写入参数boolean _status =mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);//通过transact来IPC调用_reply.readException();_result = _reply.readInt();//读取运行结果} finally {_reply.recycle();_data.recycle();}return _result;}}}
}

ITestServer 源码解析

通过android studio,可以看到该类的结构如下。

接下来我们就主要分析三个类:

  • ITestServer
    Stub和Proxy都implement了它。
    java上层可以使用这个接口来保留Binder的对象。在使用aidl的时候,完全可以只使用这个对象而不用关心这个对象是Stub还是Proxy。
  • ITestServer.Stub
    当前进程的Binder对象。
    在onTransact方法中实现了对IPC方法的逻辑,Proxy调用的时候会触发。
  • ITestServer.Stub.Proxy
    当前进程通过Proxy实现了对远端方法的调用。
    当前进程调用远端进程方法,实际上是调用了远端Binder的transact方法,最终执行到Stub中的onTransact。

ITestServer

public interface ITestServer extends android.os.IInterface {public int testFunction(java.lang.String s) throws android.os.RemoteException;----------------Stub的代码先不看
}
/*** Base class for Binder interfaces.  When defining a new interface,* you must derive it from IInterface.*/
public interface IInterface
{/*** Retrieve the Binder object associated with this interface.* You must use this instead of a plain cast, so that proxy objects* can return the correct result.*/public IBinder asBinder();
}

要点如下:

  • ITestServer本身只包括“需要IPC的方法”,此处即int testFunction(String)
  • 这个类继承了IInterface,需要实现asBinder接口。
    asBinder()会返回正确的Binder对象。如果是同进程调用就会直接返回当前进程的Binder,如果是IPC,就会返回远程调用的进程的Binder。
    对应源码就是,Stub的asBinder()返回的是this,而Proxy返回的是mRemote.

ITestServer.Stub

DESCRIPTOR

//binder唯一标识private static final java.lang.String DESCRIPTOR = "com.example.bindservicetest.ITestServer";

是Binder的唯一标识。
不同的进程之间,通过序列化传递DESCRIPTOR来找到对应的Binder。
相同进程,也需要通过DESCRIPTOR才找到对应的Binder。

TRANSACTION_testFunction

//testFunction的调用idstatic final int TRANSACTION_testFunction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

调用IPC方法的唯一id。
Stub.onTransact()中会通过TRANSACTION_testFunction来对应 执行testFunction()方法的逻辑。
Proxy调用transact的时候,也是通过传递TRANSACTION_testFunction,来标识自己想要执行的逻辑。

Stub初始化

    public Stub() {//将interface提供出去,这样当统一进程其他位置执行IBinder.queryLocalInterface的时候就可以获取到这个Binderthis.attachInterface(this, DESCRIPTOR);}
    /*** Convenience method for associating a specific interface with the Binder.* After calling, queryLocalInterface() will be implemented for you* to return the given owner IInterface when the corresponding* descriptor is requested.*/public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {mOwner = owner;mDescriptor = descriptor;}/*** Use information supplied to attachInterface() to return the* associated IInterface if it matches the requested* descriptor.*/public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {if (mDescriptor != null && mDescriptor.equals(descriptor)) {return mOwner;}return null;}

初始化时,调用attachInterface()。
attachInterface()之后,相同进程调用queryLocalInterface()的时候就能找到这个Binder。
在下面asInterface()中,就用到了queryLocalInterface()。

asInterface()

    public static com.example.bindservicetest.ITestServer asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//查找当前进程是否有这个binderif (((iin != null) && (iin instanceof com.example.bindservicetest.IServer))) {return ((com.example.bindservicetest.ITestServer) iin);}return new com.example.bindservicetest.ITestServer.Stub.Proxy(obj);}

用于将服务端的IBinder对象转化成ITestServer对象。
如果是server和client进程相同,那么会直接通过queryLocalInterface(DESCRIPTOR)找到Binder返回,这里返回的其实就是Stub对象。
如果server和client在不同进程,那么会返回一个封装后的Proxy对象。

asBinder()

    @Override public android.os.IBinder asBinder() {return this;//继承自IInterface,返回当前Stub的binder对象}

返回Stub自己。
在Proxy中返回的是远端进程的Binder。

onTransact()

    @Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)throws android.os.RemoteException {java.lang.String descriptor = DESCRIPTOR;switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(descriptor);return true;}case TRANSACTION_testFunction: {data.enforceInterface(descriptor);//需要传递DESCRIPTOR,到另一个进程可以找到对应的Binderjava.lang.String _arg0;_arg0 = data.readString();//读取参数int _result = this.testFunction(_arg0);//运行reply.writeNoException();reply.writeInt(_result);//写入返回值return true;}default: {return super.onTransact(code, data, reply, flags);}}}

实现了被IPC调用后的逻辑。
一个方法的调用在onTransact中一般有以下几个逻辑:

  1. 从data中读出参数
  2. 将读出的参数带入server的方法中,得到执行结果。
  3. 将执行结果写入reply

参数和结果为什么读写都需要通过Parcel?
进程间通信由于资源不共享,因此无法直接传递对象,只能通过序列化在不同的空间拷贝两份相同的资源。
而Parcel就是序列化的一种方案。

ITestServer.Stub.Proxy

    private static class Proxy implements com.example.bindservicetest.ITestServer {private final android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Override public android.os.IBinder asBinder() {return mRemote;//继承自IInterface,返回当前Proxy的binder对象}@Override public int testFunction(java.lang.String s) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);//需要传递DESCRIPTOR,到另一个进程可以找到对应的Binder_data.writeString(s);//写入参数boolean _status =mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);//通过transact来IPC调用_reply.readException();_result = _reply.readInt();//读取运行结果} finally {_reply.recycle();_data.recycle();}return _result;}}

Proxy初始化

      private final android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}

Proxy在初始化的时候会引用server的IBinder。

asBinder()

      @Override public android.os.IBinder asBinder() {return mRemote;//继承自IInterface,返回当前Proxy的binder对象}

Proxy在asBinder的时候会返回server的IBinder。
Stub在这个方法中会返回this。

testFunction()

      @Override public int testFunction(java.lang.String s) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);//需要传递DESCRIPTOR,到另一个进程可以找到对应的Binder_data.writeString(s);//写入参数boolean _status =mRemote.transact(Stub.TRANSACTION_testFunction, _data, _reply, 0);//通过transact来IPC调用_reply.readException();_result = _reply.readInt();//读取运行结果} finally {_reply.recycle();_data.recycle();}return _result;}

Proxy调用testFunction,实际上是通过调用mRemote.transact()来触发远端Stub的onTransact()。
一般调用的步骤如下:

  1. 创建参数与返回值的Parcel对象,将参数写入Parcel。
  2. 调用mRemote.transact(),返回值会写入到Parcel对象中。
  3. 从Parcel对象中读出返回值并return。

总结

根据上文的分析,aidl自动生成的文件的源码大概可以总结成下图:

基本步骤如下:

  1. Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。
  2. 通过Proxy来同步调用IPC方法。同时通过Parcel将参数传给Binder,最终触发Binder的transact方法。
  3. Binder的transact方法最终会触发到Server上Stub的onTransact方法。
  4. Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将结果写入Parcel后传回。
  5. Client的Ipc方法中,执行Binder的transact时,是阻塞等待的。一直到Server逻辑执行结束后才会继续执行。
  6. 当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。

继续探索

读完本文,细心的读者会发现本文还留有两个较大的问题完全没有提及:

  1. ServiceConnection是如何获取到Binder的
  2. 通过ServiceConnection获取到的Binder是什么,或者说java层的Binder是什么

这两点笔者后面的文章会继续探索,有兴趣的读者可以自行探索或者欢迎持续关注。

由浅入深 学习 Android Binder(一)- AIDL相关推荐

  1. 由浅入深 学习 Android Binder(十一) binder线程池

    Android Binder系列文章: 由浅入深 学习 Android Binder(一)- AIDL 由浅入深 学习 Android Binder(二)- bindService流程 由浅入深 学习 ...

  2. 【Android】Android Binder进程间通信AIDL示例与源码分析

    前言 众所周知,Android进程间通信采用的是Binder机制.Binder是Android系统独有的进程间通信方式,它是采用mmp函数将进程的用户空间与内核空间的一块内存区域进行映射,免去了一次数 ...

  3. aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉

    AIDL 是 Android Interface Definition Language(Android 接口定义语言)的缩写,它是 Android 进程间通信的接口语言.由于 Android 系统的 ...

  4. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  5. Android Binder基本概念流程学习

    一 Media Service进程启动 Init.rc中描述的service对应linux 的进程: Media进程定义: service media /system/bin/mediaserverc ...

  6. Android Binder通信学习

    Android Binder通信学习. 以Hello为例说明设计的几个概念关系: IhelloService.h : 提供给应用程序使用的类接口,注意是说明服务能够提供的哪些操作接口. [注意]实际不 ...

  7. 不得不说的Android Binder机制与AIDL

    说起Android的进程间通信,想必大家都会不约而同的想起Android中的Binder机制.而提起Binder,想必也有不少同学会想起初学Android时被Binder和AIDL支配的恐惧感.但是作 ...

  8. Android Binder机制学习笔记

    声明,学习材料:http://my.unix-center.net/~Simon_fu/?p=875,不是简单的copy 1    Android的进程间通讯机制(IPC)用的是自己的binder机制 ...

  9. Android Service和Binder、AIDL

    为什么80%的码农都做不了架构师?>>>    Android Service和Binder.AIDL 人收藏此文章, 关注此文章发表于3个月前 , 已有 206次阅读 共 个评论  ...

最新文章

  1. windows 下配置 react native 开发环境
  2. 微软公布19财年财报:净利润增长22%,云计算首超个人计算业务
  3. pm2集群模式mysql配置_pm2 配置方式
  4. boost::hawick_circuits用法的测试程序
  5. java反编译工具_安卓逆向之反编译工具的使用
  6. 基于Vue 和 webpack的项目实现
  7. verilog实现多周期处理器之——目录及总述
  8. 海思Hi3519AV100 emmc flash方式 linux系统移植 hitool工具烧写
  9. 百度图像识别java使用笔记
  10. 关于Windows7系统不能访问XP创建的DVD的问题
  11. 怎样用计算机计算工程量,送给用EXCEL计算工程量的朋友们一个好方法
  12. 文字烫金效果html,PS教程之3D烫金艺术文字效果制作
  13. 【工具】VScode|Linux 中怎么调试 Python 项目比较方便?又名 VScode 怎么调试 Python 项目(兼容环境Ubuntu18.04)
  14. 获取最新、最全的小红书地理位置签到数据。
  15. android画板案例
  16. Ajax异步请求之设置Content-Type
  17. AD18为PCB文件添加LOGO图标
  18. oracle税则的优先级,Oracle EBS r12财务模块
  19. 分布式数据库案例分享:腾讯移动支付平台米大师
  20. JavaScript交互式网页设计————1.JavaScript的基本语法

热门文章

  1. Android日常开发收集的Tips
  2. NTC电阻在电源输入端的应用-测试案例
  3. 基础版微信模板消息开发详解,附代码PHP
  4. 6174C语言编程,C语言代码实现:6174数学黑洞(卡普雷卡尔常数)
  5. java 获取 word 窗体域_办公小技巧:巧用窗体域 控制Word文档修改区
  6. Ajax入门-搭建服务器并使用ajax技术向服务器发送一个请求并获得服务器返回的数据
  7. 数据库中的全表扫描,索引扫描,以及相关知识点
  8. Lombok @RequiredArgsConstructor @Qualifier
  9. samba 服务器的配置基础
  10. 成为一名合格的软件测试工程师,需要具备哪些技能?