AIDL的作用是实现跨进程通讯使用方法也非常的简单,他的设计模式是典型的C/S架构。使用AIDL只要在Client端和Server端的项目根目录下面创建一个aidl的文件夹,在aidl文件夹的下面用java代码编写一个后缀名为.aidl的接口文件然后重新编译一下就会在gen目录下生成相对应的java文件。这里主要研究aidl的运作流程以及原理。

aidl结构

首先我在Server端去实现了一个aidl的接口文件代码如下:

//IMyAidl.aidl
interface IMyAidl {int add(int arg1, int aarg2);
}1234

重新编译(rebuild)就会在generated目录下面生成相应的IMyAidl.java的文件代码如下:

public interface IMyAidl extends android.os.IInterface {/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.coder_f.aidlserver.IMyAidl {private static final java.lang.String DESCRIPTOR = "com.coder_f.aidlserver.IMyAidl";/*** Construct the stub at attach it to the interface.*/public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.coder_f.aidlserver.IMyAidl interface,* generating a proxy if needed.*/public static com.coder_f.aidlserver.IMyAidl asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.coder_f.aidlserver.IMyAidl))) {return ((com.coder_f.aidlserver.IMyAidl) iin);}return new com.coder_f.aidlserver.IMyAidl.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_add: {data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.coder_f.aidlserver.IMyAidl {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic int add(int arg1, int arg2) 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.writeInt(arg1);_data.writeInt(arg2);mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public int add(int arg1, int arg2) throws android.os.RemoteException;
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798

主要就是根据aidl文件自动生成了stub的内部类,这个内部类不仅继承了Binder还继承了父类。这里继承父类主要是为了能够获得回调add接口方法,stub类并没有去真正的实现add方法。

stub类结构很清楚里面只有3个方法加上1个内部类分别是:

  • asInterface 根据传入的binder来判断当前是否是跨进程,如果跨进程则返回一个Proxy对象,如果不是跨进程则直接返回当前接口实现类(想当是调用本地接口);
  • asBinder 返回当前binder对象,继承IInterface必须实现的方法(满足内部跨进程)
  • onTransact Binder的方法当client端的Binder对象调用了transact方法时此方法会被回调
  • Proxy类 一个代理类,主要作用是配置Binder对象的transact方法(这个方法继承自IBinder)需要的参数以及调用transact方法。Proxy类也继承了我们自己定义的aidl接口,主要为了保证接口中的方法都有相对应transact去调用真正的server端方法(应为继承了接口,如果不是抽象类那就必须得去实现这些方法吧)。

这里还有一个特别的参数就是TRANSACTION_add这么一个常量,他是由一个常量+0构成的,这个常量其实就是0,这个常量主要来标记方法的如果有多个方法:

    //当前只有一个方法所以是+0,当更多的方法时每个方法都会对应一个数字。例如有两个时那就会如下static final int TRANSACTION_xxx = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);static final int TRANSACTION_yyy = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);...1234

这些方法(类)现在有一个印象就可以,后面会详细的分析。

以上是一个aidl的完整结构,当然真正要使用还需要去有一个类来继承stub类并且实现最初aidl文件中定义的所有接口(注意stub类是一个抽象类,它继承了aidl的接口但却没有真正的去实现它,这里需要我们来实现)。当然这个java类也需要在client端实现一遍(也就是自动生成一遍)。

aidl流程

要分析aidl流程所以我先创建了一个Service(MyService),在里面创建了一个内部类MyAidlImpl来继承IMyAidl.Stub并且实现我们最初定义在aidl文件中一直未被实现的add方法。在Service的onBind方法中返回这个内部类的实例(因为Stub是继承了Binder类所以MyAidlImpl的实例也会是Binder类型),具体代码如下:

Server端:MyService.java

//MyService.java
public class MyService extends Service {private MyAidlImpl myAidlImpl;public MyService() {}@Overridepublic void onCreate() {super.onCreate();myAidlImpl = new MyAidlImpl();}@Overridepublic IBinder onBind(Intent intent) {return myAidlImpl;}
}class MyAidlImpl extends IMyAidl.Stub {@Overridepublic int add(int arg1, int arg2) throws RemoteException {return arg1 + arg2;}
}123456789101112131415161718192021222324252627

接口的实现方法很简单,就是把传进来的两个参数加一下再返回回去。现在这个Service就相当于是一个Server端,来远程处理一些任务(这里就是arg1 + arg2)并且返回结果。

至于Client端代码就是最普通的一个Activity里面去绑定刚刚实现的Service,这里我为了保证是跨进程我重新创建了一个项目。(也可以在当前项目里面来做,只要在manifest文件中把Service的android:process设置为“:remote”就可以了,这样Service就不会和activity运行在一个进程中)

Client端:MainActivity.java

public class MainActivity extends AppCompatActivity {private static final String TAG = "AIDLClient";private ServiceConnection serviceConnection;private IMyAidl myAidl;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {myAidl = IMyAidl.Stub.asInterface(service);try {Log.d(TAG, "onServiceConnected: data from server = " + myAidl.add(123, 456));} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {myAidl = null;}};bindService(new Intent("com.coder_f.aidlserver.MyService"), serviceConnection, BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(serviceConnection);}
}123456789101112131415161718192021222324252627282930313233343536

根据上面代码我们现在看一下,首先是Client端来bindService,这里绑定成功以后就会返回一个Binder对象,这个对象就是Server端onBind的时候返回的,是MyAidlImpl的实例,通过IMyAidl.Stub.asInterface(service)方法来获得一个Proxy类的对象(这里考虑的是跨进程调用的情况所以返回的是proxy对象),然后我们调用了proxy对象里面的add方法,那我们就跟着去看一下根据aidl自动生成的java类中proxy的add方法里面具体是什么逻辑。

 private static class Proxy implements com.coder_f.aidlserver.IMyAidl {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic int add(int arg1, int arg2) 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.writeInt(arg1);_data.writeInt(arg2);mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;}}1234567891011121314151617181920212223242526272829303132333435

这里的remote就是asInterfa方法中传入的Binder对象,通过这个Binder对象,接着调用了add方法。这里来仔细看看这个add方法的逻辑,总共就三步。

  1. 创建了两个Parcel对象(跨进程只能传递Parcel以及Binder类型的对象)_data用来存放参数,_reply用来存放返回值。这里传入的DESCRIPTOR是一个包名。这个DESCRIPTOR也起到标识的作用,只有transact和onTransact方法中parcel对象的一致时才能成功调用。这也是为什么我们要求Server端和Client端aidl存放目录结构一致的原因,不一致的话这个自动生成DESCRIPTOR也会不一样;
  2. 调用了Binder的transact方法,这个方法其实就是跨进程调用的核心,他的第一个参数是一个int值,用来告诉Server端(onTransact方法)我要调用的具体是哪个方法,这里Stub.TRANSACTION_add标志的就是add方法,第二个和第三个之前已经说了分别是参数和返回值,第四个是个标志位,当0的时候代表有返回值,当1的时候则代表没有返回值(此时就算你的方法真的有返回值_reply中也不会有结果)
  3. 读取_reply中返回的结果。

这里要注意的是当调用transact方法时当前线程会被挂起。此时如果这个方法运行在主线程时,而server端又因为各种原因或者方法本身就是耗时的导致不能及时返回结果就会使得ui线程卡住。而我上面Client端方法中myAidl.add(123,456)是放在了onServiceConnected方法里面,而onServiceConnected方法其实是运行在主线程的(具体可以看我之前bindservice流程分析文章中最后面的说明)

这里调用Binder的transact方法以后经过一系列的流程(这里具体什么流程没有仔细研究过,涉及到jni底层native的binder方法)最后会回调到Server端的onTransact方法。

好了那我们就看看自动给我们生成的java文件中onTransact方法的逻辑代码如下。

 @Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_add: {data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}
12345678910111213141516171819202122

这里的onTransact方法的参数其实和transact方法的参数是一一对应的,第一个就是标志方法的code默认会有有一个INTERFACE_TRANSACTION用来验证之前说的DESCRIPTOR,另外一个就是TRANSACTION_add,这里我们接口中只有一个所以总共onTransact方法中只对这一个方法处理。

这个add方法和proxy类中add方法的结构很类似,就是从_data中取出(proxy.add是创建写入)参数调用真正add方法(在service中实现的add方法)然后把结果写入到reply(proxy.add是创建读取)中。注意这个方法有一个返回值,如果返回false则Client端会调用失败。

上面就是aidl的一整套流程。

总结:

  • aidl其实就是利用Binder来实现跨进程调用。而这个系统根据.aidl文件生成的java文件就是对binder的transact方法进行封装。

  • onTransact方法是提供给server端用的,transact方法(内部类proxy封装了transact方法)和asInterface方法是给client端用的。系统自动生成不知道你要把哪个当做server端哪个当做client端所以就都生成了。

  • DESCRIPTION是根据aidl文件位置(包名)来生成的,DESCRIPTION是binder的唯一标识,这也解释了为什么要保证client端和server端.adil文件的包结构一样;

  • aidl的transact的调用会导致当前线程挂起,因此如果Server端的方法耗时最好另起线程来调用transact方法;

  • aidl通过Parcel来传递参数和返回值,因为binder只支持Binder对象和parcel对象的跨进程调用;

  • Binder,IBinder,IInterface三个的关系。Binder是继承自IBinder对象的,IBinder是远程对象的基本接口。另外IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact(),而IInterface主要为aidl服务,里面就一个方法——asBinder,用来获得当前binder对象(方便系统内部调用)。

    最后说白了aidl的核心就是client调用binder的transact方法,server通过binder的onTransact方法了来执行相应的方法并返回。

Binder相关面试总结(七):AIDL内部的实现原理是什么相关推荐

  1. Binder相关面试总结(六):四大组件底层的通信机制是怎样的

    一.前言 这篇文章我酝酿了很久,参考了很多资料,读了很多源码,却依旧不敢下笔.生怕自己理解上还有偏差,对大家造成误解,贻笑大方.又怕自己理解不够透彻,无法用清晰直白的文字准确的表达出 Binder 的 ...

  2. Binder相关面试总结(二):Binder到底是什么?

    Binder单从字面上理解,它有活页夹,粘合剂的意思,活页夹可以用来把两个东西夹在一起.在我们的Android系统中,Binder主要用来实现进程之间的通信(IPC),它的主要作用就是把多个App夹在 ...

  3. Binder相关面试总结(四):一次Binder通信的基本流程是什么样?

    概述 AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成可以在Android设备上两个进程之间进行PC的代码.如果在一个进程中(例如 ...

  4. Binder相关面试总结(三):Binder机制是如何跨进程的

    Android中进程和线程的关系和区别 线程是CPU调度的最小单元,同时线程是一种有限的系统资源:而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用. 进程有自己独立的地址空间,而进程 ...

  5. Binder相关面试总结(一):为什么Android要采用Binder作为IPC机制?

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nwy9SoNo-1609925310525)(//upload-images.jianshu.io/upload_ima ...

  6. Binder相关面试题目

    参考学习视频:2021字节跳动.腾讯.阿里巴巴.美团.OPPO.华为.百度等一线 目录 问题一:Binder有什么优势?(字节跳动) 问题二:Binder是如何做到一次拷贝?(腾讯) 问题三:MMAP ...

  7. Binder相关面试总结(五):为什么Activity间传递对象需要序列化

    前言 我们都知道进行Android 开发的时候,跳转到Activity和Fragment的时候,传递对象是通过Intent或者bundle 进行传递.当这个对象没有实现序列化的时候 当你通过Inetn ...

  8. python线程进程协程面试_Python学习经验之谈:关于协程的理解和其相关面试问题...

    都知道Python非常适合初学者学习来入门编程,昨天有伙伴留言说面试了Python岗位,问及了一个关于协程的问题,想了想还是跟大家出一篇协程相关的文章和在Python面试中可能会问及的相关面试问题.都 ...

  9. 23种经典设计模式都有哪些,如何分类?Java设计模式相关面试

    23种经典设计模式都有哪些,如何分类? 23种经典设计模式都有哪些,如何分类? java常用的设计模式?说明工厂模式 Java 中的23 种设计模式: Factory(工厂模式), Builder(建 ...

最新文章

  1. qt vs 不出来dos窗口_VS嵌入QT后,建立QT工程后printf和cout无效,无法产生控制台应用程序窗口,需设置工程属性...
  2. Hibernate n+1问题
  3. pycharm同一目录下无法import其他文件
  4. 敲代码括号技巧_理解代码块概念,养成良好编程习惯 | 亲子课堂 第 3 课
  5. webstorm jquery语法提示_WebStorm快速入门指南,开发者必备!
  6. 【Vue2.0】—过渡与动画(二十一)
  7. Linux学习笔记 -- 定时任务调度/磁盘分区与挂载
  8. cmake之交叉编译arm32/arm64(四)
  9. 《软件工具》分享2款好用的时序图工具
  10. pdf转word思路和方法
  11. 详解 Kubernetes ReplicaSet 的实现原理
  12. C# Windows Service与Timer(计时器)
  13. MCS-51单片机结构学习总结
  14. 【未】Dynamic incentive schemes for managing dockless bike-sharing systems
  15. 为什么PDF文件无法编辑?
  16. html火狐浏览器秒杀插件,火狐浏览器Firefox(已装载常用渗透插件) 2018-06
  17. FFMPEG 将IPCamera的RTSP视频流传送至RED5服务器 小白教程
  18. ImportError: packaging>=20.0 is required for a normal functioning of this mo
  19. win10 uwp iot
  20. TrafficMonitorr网速流量监控/CPU内存率查看工具

热门文章

  1. python request timeout是什么意思_request timeout是什么意思
  2. java按钮监听休眠_java-休眠监控解决方案
  3. 1000在计算机术语是什么意思,计算机里所提及的1k字是什么
  4. python面向对象类属性_python面向对象之类属性和类方法案例分析
  5. 三星自动驾驶汽车路测获批,进军无人驾驶领域
  6. [转] Android系统版本号和Android API level对应表
  7. Linux 学习记录 四(Bash 和 Shell scirpt).
  8. Nginx 代理 WebSocket
  9. 64位开源处理器Rocket该人士介绍
  10. Fork 一个仓库并同步