Android AIDL 原理解析

如果去阅读Android的源代码,就会发现里面大量用到了Binder、AIDL相关知识,比如当我们去使用AMSPMSWMS这些核心服务,因为他们都运行在 system_server 进程,普通应用想调用他们提供的服务(例如:startActivity(),就需要AMS来实现),就必须要跨进程调用,因此,我们在阅读代码之前,必须先去尝试理解Binder、AIDL相关知识。

为什么要使用AIDL呢?

通过AIDL,可以让本地调用远程服务器的接口就像调用本地接口那么简单,让用户无需关注内部细节,只需要实现自己的业务逻辑接口,内部复杂的参数序列化发送、接收、客户端调用服务端的逻辑,你都不需要去关心了。

一 从一个例子开始

我们通过一个简单的跨进程调用的例子来理解AIDL。

设计一个简单的场景:

我们有一个CoreService运行在":core"进程中,提供文件下载服务,我们会在Activity中去bind这个Service,并且调用它为我们提供的服务。

实现起来很简单,只需要以下几个步骤:

  • 定义一个AIDL文件
 interface IDownloadService {/*** 下载* @param url*/void download(String url);/*** 删除下载任务* @param url*/void delete(String url);/*** 停止下载任务* @param url*/void stop(String url);/*** 获取下载队列大小* @return*/int getQueueSize();
}
  • 通过AIDL的代码生成器,生成实现Proxy、Stub代码逻辑 以Android Studio为例,自动生成的IDownloadService.java位于\app\build\generated\source\aidl\debug目录下,核心方法我已添加注释。
public interface IDownloadService extends android.os.IInterface {/*** 下载** @param url*/public void download(java.lang.String url) throws android.os.RemoteException;/*** 删除下载任务** @param url*/public void delete(java.lang.String url) throws android.os.RemoteException;/*** 停止下载任务** @param url*/public void stop(java.lang.String url) throws android.os.RemoteException;/*** 获取下载队列大小** @return*/public int getQueueSize() throws android.os.RemoteException;/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.cundong.touch.IDownloadService {// 注意,通过以下代码我们发现,我们在.aidl中定义的方法名字其实没什么意义,服务器端根本没有使         // 用这些名字,而是自动为这些方法生成了递增的ID,根据方法的顺序而不是名字来一个一个处理static final int TRANSACTION_download = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_delete = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);static final int TRANSACTION_getQueueSize = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);private static final java.lang.String DESCRIPTOR = "com.cundong.touch.IDownloadService";/*** Construct the stub at attach it to the interface.*/public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.cundong.touch.IDownloadService interface,* generating a proxy if needed.* * 注:* 这个方法里面有个处理,通过queryLocalInterface查询,如果服务端和客户端都是在同一个进程,那          * 么就不需要跨进程了,直接将IDownloadService当做普通的对象来使用即可,否则返回远程对象* 的代理*/public static com.cundong.touch.IDownloadService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.cundong.touch.IDownloadService))) {return ((com.cundong.touch.IDownloadService) iin);}return new com.cundong.touch.IDownloadService.Stub.Proxy(obj);}/*** 返回Binder实例*/@Overridepublic android.os.IBinder asBinder() {return this;}/*** 根据code参数来处理,这里面会调用真正的业务实现类*/@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_download: {data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();this.download(_arg0);reply.writeNoException();return true;}case TRANSACTION_delete: {data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();this.delete(_arg0);reply.writeNoException();return true;}case TRANSACTION_stop: {data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();this.stop(_arg0);reply.writeNoException();return true;}case TRANSACTION_getQueueSize: {data.enforceInterface(DESCRIPTOR);int _result = this.getQueueSize();reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.cundong.touch.IDownloadService {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}//Proxy的asBinder()返回位于本地接口的远程代理@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}/*** 下载** @param url*/@Overridepublic void download(java.lang.String url) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(url);//Proxy中, 业务接口中其实没有做任何业务逻辑处理,仅仅是收集本地参数,序列化//后通过IBinder的transact方法,发给服务器端,并且通过_reply来接收服务器端//的处理结果mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}/*** 删除下载任务** @param url*/@Overridepublic void delete(java.lang.String url) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(url);mRemote.transact(Stub.TRANSACTION_delete, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}/*** 停止下载任务** @param url*/@Overridepublic void stop(java.lang.String url) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(url);mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}/*** 获取下载队列大小** @return*/@Overridepublic int getQueueSize() 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);mRemote.transact(Stub.TRANSACTION_getQueueSize, _data, _reply, 0);_reply.readException();_result = _reply.readInt();} finally {_reply.recycle();_data.recycle();}return _result;}}}
}
  • 继承IDownloadService.Stub,实现真正的服务器端接口
public class IDowanloadServiceImpl extends IDownloadService.Stub {private final Random mGenerator = new Random();@Overridepublic void download(String url) throws RemoteException {//TODO 真正的业务逻辑}@Overridepublic void delete(String url) throws RemoteException {//TODO 真正的业务逻辑}@Overridepublic void stop(String url) throws RemoteException {//TODO 真正的业务逻辑}@Overridepublic int getQueueSize() throws RemoteException {//TODO 真正的业务逻辑return mGenerator.nextInt(100);}
}
  • 远程Service的onBind()接口返回Stub的IBinder
public class CoreService extends Service {@Nullable@Overridepublic IBinder onBind(Intent intent) {return new IDowanloadServiceImpl();}
}
  • 客户端发起远程调用
private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName className, IBinder service) {sIDownloadService = IDownloadService.Stub.asInterface(service);mBound = true;}@Overridepublic void onServiceDisconnected(ComponentName arg0) {mBound = false;}};
//调用
if (mBound) {int size = 0;try {size = sIDownloadService.getQueueSize();} catch (RemoteException e) {e.printStackTrace();}}

二 原理分析

通过阅读自动生成的IDownloadService.java,可以看出,这是一个经典的代理模式架构。AIDL的代码生成器,已经根据.aidl文件自动帮我们生成Proxy、Stub(抽象类)两个类,并且把客户端代理mRemote的transact()过程以及 服务器端的onTtransact()过程默认实现好了,我们只需要在服务器端继承Stub,实现自己的业务类(在onTtransact()中会调用)。

1. UML图

2. 代码分析

代码主要分为Proxy、Stub两部分。

1) Proxy

Proxy运行在客户端,它实现了IDownloadService接口,并且持有一个远程代理IBinder mRemote,mRemote不做任何业务逻辑处理,仅仅通过IBinder接口的transact()方法,把客户端的调用参数序列化后transact到远程服务器。

示例:

@Override
public void download(java.lang.String url) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(url);mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}
}

_data即调用接口传入的参数,_reply为调用方法得到的返回值,mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);为调用过程。

2) Stub

Stub运行在服务器端,继承自Binder,同样也实现了IDownloadService接口,它的核心逻辑在onTransact方法中:

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {switch (code) {case TRANSACTION_download: {data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();this.download(_arg0);reply.writeNoException();return true;}case TRANSACTION_delete: {...}case TRANSACTION_stop: {...}case TRANSACTION_getQueueSize: {...}}return super.onTransact(code, data, reply, flags);
}

远程服务器端通过IBinder接口的onTtransact()方法来接收数据、处理数据,并且调用真实的业务逻辑代码(如上面的download接口),通过reply像客户端传递返回值。

另外,Stub中另外一个比较重要的接口就是asInterface()接口,我们在客户端真正使用的时候通常会这样使用它:

IDownloadService sIDownloadService = IDownloadService.Stub.asInterface(IBinder service);

通过方法名字,我们大致可以猜出,它大概实现的功能,就是将一个IBinder对象转化为接口对象IDownloadService,通过代码可以看到具体逻辑:

public static com.cundong.touch.IDownloadService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.cundong.touch.IDownloadService))) {return ((com.cundong.touch.IDownloadService) iin);}return new com.cundong.touch.IDownloadService.Stub.Proxy(obj);}

先通过queryLocalInterface查询,如果服务端和客户端都是在同一个进程,那么就不需要跨进程了,直接将IDownloadService当做普通的对象来使用,否则会返回远程对象的代理对象。

至此,我们就通过源码层面了解了AIDL是如何工作了。

Android AIDL 原理解析相关推荐

  1. 爱加密Android APk 原理解析

    转载请标明出处:http://blog.csdn.net/u011546655/article/details/45921025 爱加密Android APK加壳原理解析 一.什么是加壳? 加壳是在二 ...

  2. Android AIDL实例解析

    AIDL这项技术在我们的开发中一般来说并不是很常用,虽然自己也使用新浪微博的SSO登录,其原理就是使用AIDL,但是自己一直没有动手完整的写过AIDL的例子,所以就有了这篇简单的文章. AIDL(An ...

  3. Android—Gson原理解析

    JsonElement 抽象类 代表json串的某一个元素 某一个元素: JsonObject JsonArray JsonPrimitive(基本类型) JsonNull JsonElement的四 ...

  4. Android livedata原理解析之自己实现一个简单的LiveData

    LiveData其实就是通过管理生命周期来实现当视图不可见时不渲染数据,当视图可见时再渲染数据.当在一个Activity发送一个网络请求后,立马切到另一个界面或都按下Home键,使得视图不可见..这个 ...

  5. android asynctask,Android AsyncTask原理解析

    想要启动一个AsyncTask,首先需要创建一个AsyncTask对象然后调用execute方法.例如: new DownloadFilesTask().execute(); DownloadFile ...

  6. BAT大量裁人,快35岁的程序员该何去何从,Android并发原理解析

    行业前景 首先我给大家分析一下,现在行业的一个情况. 2018年IT界规模最大的裁员事件了.近日,面对难看的财务报表,美国最大的通讯运营商.市值2200亿美金的Verizon,遣散了4.4万名老员工. ...

  7. Android悬浮窗原理解析(Window)[源码]

    悬浮窗,在大多数应用中还是很少见的,目前我们接触到的悬浮窗,差不多都是一些系统级的应用软件,例如:360安全卫士,腾讯手机管家等:在某些服务行业如金融,餐饮等,也会在应用中添加悬浮窗,例如:美团的偷红 ...

  8. Android之Butterknife原理解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! ##前言 Butterknife是一个专注于Android系统的View注入框架, ...

  9. Android 插件化原理解析——Activity生命周期管理

    之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...

  10. Android代码入侵原理解析(一)

    Original 2017-05-06 付超红 滴滴安全应急响应中心 2017年初,在滴滴安全沙龙上,滴滴出行安全专家--付超红,针对App的攻与防进行了分享.会后大家对这个议题反响热烈,纷纷求详情求 ...

最新文章

  1. 小米6鲁大师html5评测,一加6T依然是顶级旗舰!鲁大师2018手机性能榜跑分排前三!...
  2. c#调用java开发的webservice_用C#.NET调用Java开发的WebService传递int,double问题
  3. dbms_job涉及到的知识点
  4. Mentor PADS 9.5下载安装及破解指南
  5. UESTC 1636 梦后楼台高锁,酒醒帘幕低垂
  6. Java基础入门笔记-整数+小数+字符串+打印
  7. python读取linux内存_使用python获取CPU和内存信息(linux系统)
  8. Git子模块头#39;引用不是树#39;错误
  9. 《数据结构:c语言版》(严蔚敏)知识点整合
  10. IDEA几款不错的基于Darcula的深色主题
  11. 用firework中合并图标的方法
  12. python爬虫微信_python 微信爬虫
  13. k8s技术预研11--kubernetes网络原理
  14. Excel学习笔记:P1-Excel入门
  15. 聚焦应对最新网络安全挑战,2022 BLACK HAT- OMDIA分析师大会报告开放申请
  16. 计算机开启蓝牙网络,怎么打开电脑蓝牙功能(笔记本电脑蓝牙怎么开)
  17. 2020CTF笔记crypto部分
  18. 如何查询计算机已连接wife的密码错误,电脑已经连上无线如何查看WIFI密码
  19. Wu-Manber算法
  20. 鼠标不能再Linux命令界面滚动,在linux中,鼠标的滚轮怎么无法使用

热门文章

  1. (15)FPGA与CPU区别
  2. FPGA20个例程专栏介绍
  3. mysql添加字段语句_mysql增加字段 mysql数据库更新字段语句
  4. win10无法修改mac地址_电脑MAC地址(物理地址)修改方法
  5. 【日常点滴015】python中学完pandas后的代码练习 附源数据文件
  6. 淘宝商品描述信息查询API接口(淘宝商品详情API接口)
  7. NoSQL:文档数据库
  8. 6轴并联机器人开发--机械设计
  9. GitHub:一款基于OCR技术的翻译器
  10. 基于单片机的电子万年历的设计