当前位置: 首页 > news >正文

Android下AIDL机制详解

AIDL全名Android Interface Definition Language,是一种接口定义语言,也是Android系统的一种跨进程通信机制。从AIDL的名字就可以看出来,AIDL做的就是在服务提供进程和服务使用进程之间的协商好的接口,双方通过该接口进行通信。本文将以一个例子来讲述AIDL的使用方式和流程,在下一篇文章中我将从代码层面对AIDL进行分析。

AIDL实例

文章中所涉及的例子来源于android开发之AIDL用法_进程间通信原理详解 一文。首先我们创建一个AIDL文件,并创建了一个接口方法:

interface forService {  
    void registerTestCall(forActivity cb);  
    void invokCallBack();  
} 

这里多说一句,在AIDL文件中并不是所有的数据都可以使用,能够使用的数据类型包括如下几种:

    * 基本数据类型(int, long, char, boolean, double等)
    * String和CharSequence
    * List:只支持ArrayList,并且里面的元素都能被AIDL支持
    * Map:只支持HashMap,里面的每个元素能被AIDL支持
    * Parcelable:所有实现Parcelable接口的对象
    * AIDL: 所有AIDL接口本身也能在AIDL文件中使用

另外AIDL中除了基本数据类型意外,其他数据类型必须标上方向:in、out或者inout。其实AIDL文件和interface很像,其作用本质也是定义了一个接口,就像双方制定的一个协议一样,在通信时必须遵守该协定才能正常通信。

远程服务端Service实现

package com.styleflying.AIDL;  
import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.os.RemoteCallbackList;  
import android.os.RemoteException;  
import android.util.Log;  
public class mAIDLService extends Service { 
    ....  
    @Override  
    public void onCreate() {  
        Log("service create");  
    }   
      
    @Override  
    public IBinder onBind(Intent t) {  
        Log("service on bind");  
        return mBinder;  
    }   
    @Override  
    public boolean onUnbind(Intent intent) {  
        Log("service on unbind");  
        return super.onUnbind(intent);  
    }  
    public void onRebind(Intent intent) {  
        Log("service on rebind");  
        super.onRebind(intent);  
    }  
    private final forService.Stub mBinder = new forService.Stub() {  
        @Override  
        public void invokCallBack() throws RemoteException  
        {  
            callback.performAction();  
              
        }  
        @Override  
        public void registerTestCall(forActivity cb) throws RemoteException  
        {  
            callback = cb;  
              
        }  
          
    };  
} 

这里注意一下onBind()函数,该函数返回了一个IBinder类型,IBinder说明AIDL底层是基于Android Binder机制实现的,Binder机制的具体实现细节放到下一篇博客再做详细介绍。onBind函数实际返回的是mBinder类型,而该类型实际是声明了一个forService.stub类型,并在声明中对AIDL文件定义的接口方法做了具体的实现。所以这里的mBinder可以理解为一个包含了AIDL所定义接口具体实现的类,而这个类最终将传递给客户端,供其调用。

客户端代码

    package com.styleflying.AIDL;  
    .... 
    public class mAIDLActivity extends Activity {  
        private static final String TAG = "AIDLActivity";    
        forService mService;  
        private ServiceConnection mConnection = new ServiceConnection() {  
            public void onServiceConnected(ComponentName className,  
                    IBinder service) {  
                mService = forService.Stub.asInterface(service);  
                try {  
                    mService.registerTestCall(mCallback);}  
                catch (RemoteException e) {  
                      
                }  
                }  
            public void onServiceDisconnected(ComponentName className) {  
                Log("disconnect service");  
                mService = null;  
                }  
            };     
    }  

ServiceConnection 是客户端发起的一个指向服务端的连接,而在连接成功时(onServiceConnected被调用时),通过
mService =forService.Stub.asInterface(service)获取到了服务端传递过来的包含有AIDL规定接口具体实现的AIDL对象(即service端onBind返回的对象,该对象原本是一个forService.stub对象通过调用asInterface接口获取到了对应的AIDL对象),接下来就可以利用mService调用对应的方法了。然后在连接断开时再释放即可。

AIDL源码解析

上文即是一个AIDL的使用实例,利用AIDL可以轻松的实现在Android端的跨应用通信。但知其然还要知其所以然,这样简单的使用显然无法透彻的了解AIDL通信的原理。上文我们已经提到了AIDL实际底层利用的是Android Binder机制进行通信,在本文中我们将从代码层面继续剖析AIDL机制,而在下一篇博客中讲解Binder机制的原理。虽然我们定义的AIDL文件只有寥寥数行,但是真正的运行起来的AIDL代码却远远不止这点,实际上大部分的工作IDE都帮我们完成了,以上文中的forService为例,在我们编写完AIDL文件后,IDE会生成其对应的java文件forService.java

package ...;  
import java.lang.String;  
import android.os.RemoteException;  
import android.os.IBinder;  
import android.os.IInterface;  
import android.os.Binder;  
import android.os.Parcel;  
public interface forService extends android.os.IInterface  
{  
/** Local-side IPC implementation stub class. */  
public static abstract class Stub extends android.os.Binder implements com.styleflying.AIDL.forService  
{  
private static final java.lang.String DESCRIPTOR = "com.styleflying.AIDL.forService";  
/** Construct the stub at attach it to the interface. */  
public Stub()  
{  
this.attachInterface(this, DESCRIPTOR);  
}  
/** 
 * Cast an IBinder object into an forService interface, 
 * generating a proxy if needed. 
 */  
public static com.styleflying.AIDL.forService asInterface(android.os.IBinder obj)  
{  
if ((obj==null)) {  
return null;  
}  
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);  
if (((iin!=null)&&(iin instanceof com.styleflying.AIDL.forService))) {  
return ((com.styleflying.AIDL.forService)iin);  
}  
return new com.styleflying.AIDL.forService.Stub.Proxy(obj);  
}  
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  
{  
switch (code)  
{  
case INTERFACE_TRANSACTION:  
{  
reply.writeString(DESCRIPTOR);  
return true;  
}  
case TRANSACTION_registerTestCall:  
{  
data.enforceInterface(DESCRIPTOR);  
com.styleflying.AIDL.forActivity _arg0;  
_arg0 = com.styleflying.AIDL.forActivity.Stub.asInterface(data.readStrongBinder());  
this.registerTestCall(_arg0);  
reply.writeNoException();  
return true;  
}  
case TRANSACTION_invokCallBack:  
{  
data.enforceInterface(DESCRIPTOR);  
this.invokCallBack();  
reply.writeNoException();  
return true;  
}  
}  
return super.onTransact(code, data, reply, flags);  
}  
private static class Proxy implements com.styleflying.AIDL.forService  
{  
private android.os.IBinder mRemote;  
Proxy(android.os.IBinder remote)  
{  
mRemote = remote;  
}  
public android.os.IBinder asBinder()  
{  
return mRemote;  
}  
public java.lang.String getInterfaceDescriptor()  
{  
return DESCRIPTOR;  
}  
public void registerTestCall(com.styleflying.AIDL.forActivity cb) 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.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));  
mRemote.transact(Stub.TRANSACTION_registerTestCall, _data, _reply, 0);  
_reply.readException();  
}  
finally {  
_reply.recycle();  
_data.recycle();  
}  
}  
public void invokCallBack() throws android.os.RemoteException  
{  
android.os.Parcel _data = android.os.Parcel.obtain();  
android.os.Parcel _reply = android.os.Parcel.obtain();  
try {  
_data.writeInterfaceToken(DESCRIPTOR);  
mRemote.transact(Stub.TRANSACTION_invokCallBack, _data, _reply, 0);  
_reply.readException();  
}  
finally {  
_reply.recycle();  
_data.recycle();  
}  
}  
}  
static final int TRANSACTION_registerTestCall = (IBinder.FIRST_CALL_TRANSACTION + 0);  
static final int TRANSACTION_invokCallBack = (IBinder.FIRST_CALL_TRANSACTION + 1);  
}  
public void registerTestCall(com.styleflying.AIDL.forActivity cb) throws android.os.RemoteException;  
public void invokCallBack() throws android.os.RemoteException;  
}  

代码比较复杂,而且一看就是系统自动生成的,看起来很不规范。不用着急,我们一点一点来啃。首先这个类在内部实现了一个抽象类Stub,这个词看起来是不是很熟悉,对了,它就是在Service端出现的那个stub。stub直译过来是存根,也就是Service保留在本地的一个凭证。forService类中所有的逻辑都在stub中实现。而在stub中又有一个子类称为proxy(代理),这个类名更好理解,他是service的代理,需要用到代理的地方只有远程的调用者,即客户端。所以proxy就是从服务端传递到客户端的对象,客户端正是通过这个代理来执行AIDL中的接口方法的。

Stub

接下来我们深入两个类来看其代码,首先stub通过android.os.IInterfaceattachInterface方法来完成自我构造。接下来是asInterface方法,客户端正是利用这个方法来获取到远程服务对象的。在该方法中,首先是在本地查询是否有DESCRIPTOR所描述的类,如果存在直接返回;如果不存在就返回proxy。这说明当客户端在调用该方法获取远程服务时,实际上服务端首先是会检查服务端是否和客户端在同一个进程中,如果在则直接返回自身,如果不是则返回proxy代理。
onTransact是指令的执行的函数,也即执行AIDL接口定义的函数实际上最终都会执行到这里。data是传入数据,reply是返回数据,两个数据都是Parcel类说明在AIDL的通信过程中数据必须经过序列化操作。不同的指令执行不同的switch分支:1)读取数据;2)执行指令;3)返回数据。

Proxy

读懂了Stub的源码,Proxy的源码就更加简单了,Proxy类中有一个mRemote属性,该属性就是远端的服务端stub。当客户端利用proxy代理执行对应的方法时,proxy的执行逻辑都是:1)声明传入数据和返回结果两个序列化对象;2)写入传入数据;3)执行方法,执行的逻辑就是调用mRemote的onTransact方法在远端执行方法;4)执行完成后读取返回结果

以上就是从代码层面来解析AIDL机制的实现原理,其实如果读懂了代码,AIDL的实现原理也不难,stub-proxy模式也是一个常用的设计模式。但是数据和指令究竟是怎么在两个进程之间进行传递的,源码中却没有体现,这就需要了解AIDL的底层实现机制Binder了。

相关文章:

  • 1614: [Usaco2007 Jan]Telephone Lines架设电话线
  • DataGridView的按钮列的点击事件
  • MVC--数据增删改查(Razro语法)
  • 【node学习】协程
  • 【转】JVM 分代GC策略分析
  • centos下编译安装MySQL5.7.16
  • Meta标签
  • OpenCV例程实现人脸检测
  • bt和wifi的共存
  • 使用Powershell链接到Office 365
  • Bootstrap-datepicker设置开始时间结束时间范围
  • django中的filter详解
  • CDN学习笔记二(技术详解)
  • macOS 中的 Rootless 机制
  • python环境搭建-设置PyCharm软件的配色方案和Python解释器
  • 《剑指offer》分解让复杂问题更简单
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Angular2开发踩坑系列-生产环境编译
  • CODING 缺陷管理功能正式开始公测
  • JSDuck 与 AngularJS 融合技巧
  • JSONP原理
  • Markdown 语法简单说明
  • nginx 负载服务器优化
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 基于 Babel 的 npm 包最小化设置
  • 码农张的Bug人生 - 初来乍到
  • 前端js -- this指向总结。
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 最简单的无缝轮播
  • 进程与线程(三)——进程/线程间通信
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ###项目技术发展史
  • #数学建模# 线性规划问题的Matlab求解
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .NET 4.0中的泛型协变和反变
  • .net2005怎么读string形的xml,不是xml文件。
  • .Net多线程总结
  • .vue文件怎么使用_vue调试工具vue-devtools的安装
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • [ IOS ] iOS-控制器View的创建和生命周期
  • [ solr入门 ] - 利用solrJ进行检索
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具
  • [1525]字符统计2 (哈希)SDUT
  • [52PJ] Java面向对象笔记(转自52 1510988116)
  • [Android 数据通信] android cmwap接入点
  • [Big Data - Kafka] kafka学习笔记:知识点整理