Android : 跟我学Binder --- (2) AIDL分析及手动实现
目录:
Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?
Android : 跟我学Binder --- (2) AIDL分析及手动实现
Android : 跟我学Binder --- (3) C程序示例
Android : 跟我学Binder --- (4) 驱动情景分析
Android : 跟我学Binder --- (5) C++实现
Android : 跟我学Binder --- (6) JAVA实现
一、关于Android日常开发中进程间通信-AIDL
通常Android应用开发中实现进程间通信用的最多的就是 AIDL,借助 AIDL 编译以后的代码能帮助我们进一步理解 Binder IPC 的通信原理。但是无论是从可读性还是可理解性上来看,编译器生成的代码对开发者并不友好。比如一个 INanoMethod.aidl 文件对应会生成一个 INanoMethod.java 文件,这个 java 文件包含了一个 INanoMethod接口、一个 Stub 静态的抽象类和一个 Proxy 静态类。Proxy 是 Stub 的静态内部类,Stub 又是 INanoMethod的静态内部类,这就造成了可读性和可理解性的问题。
INanoMethod.aidl 内容:
package com.android.NanoServer;
import com.android.NanoServer.INanoMethodCallback;
// Declare any non-default types here with import statements interface INanoMethod {
/**
* 注册INanoVoiceCallback相关回调接口.
*
* @param INanoVoiceCallback 回调接口
*/
void registerCallBack( INanoMethodCallback callback);
/**
* 解除INanoVoiceCallback相关回调接口.
*
* @param INanoVoiceCallback 回调接口
*/
void unregisterCallBack(INanoMethodCallback callback);
/**
* 保存Activity对象.
*/
void registerActivity(); }
通过编译器生产的 INanoMethod.java:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\work\\Android\\project\\androidStudio\\BLE-GATT-\\aidl\\src\\main\\aidl\\com\\android\\NanoServer\\INanoMethod.aidl
*/
package com.android.NanoServer;
// Declare any non-default types here with import statements public interface INanoMethod extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.NanoServer.INanoMethod
{
private static final java.lang.String DESCRIPTOR = "com.android.NanoServer.INanoMethod";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.NanoServer.INanoMethod interface,
* generating a proxy if needed.
*/
public static com.android.NanoServer.INanoMethod asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.NanoServer.INanoMethod))) {
return ((com.android.NanoServer.INanoMethod)iin);
}
return new com.android.NanoServer.INanoMethod.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_registerCallBack:
{
data.enforceInterface(descriptor);
com.android.NanoServer.INanoMethodCallback _arg0;
_arg0 = com.android.NanoServer.INanoMethodCallback.Stub.asInterface(data.readStrongBinder());
this.registerCallBack(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterCallBack:
{
data.enforceInterface(descriptor);
com.android.NanoServer.INanoMethodCallback _arg0;
_arg0 = com.android.NanoServer.INanoMethodCallback.Stub.asInterface(data.readStrongBinder());
this.unregisterCallBack(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_registerActivity:
{
data.enforceInterface(descriptor);
this.registerActivity();
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.android.NanoServer.INanoMethod
{
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;
}
/**
* 注册INanoVoiceCallback相关回调接口.
*
* @param INanoVoiceCallback 回调接口
*/
@Override public void registerCallBack(com.android.NanoServer.INanoMethodCallback callback) 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((((callback!=null))?(callback.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerCallBack, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
/**
* 解除INanoVoiceCallback相关回调接口.
*
* @param INanoVoiceCallback 回调接口
*/
@Override public void unregisterCallBack(com.android.NanoServer.INanoMethodCallback callback) 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((((callback!=null))?(callback.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unregisterCallBack, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
/**
* 保存Activity对象.
*/
@Override public void registerActivity() 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_registerActivity, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_registerCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_unregisterCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerActivity = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* 注册INanoVoiceCallback相关回调接口.
*
* @param INanoVoiceCallback 回调接口
*/
public void registerCallBack(com.android.NanoServer.INanoMethodCallback callback) throws android.os.RemoteException;
/**
* 解除INanoVoiceCallback相关回调接口.
*
* @param INanoVoiceCallback 回调接口
*/
public void unregisterCallBack(com.android.NanoServer.INanoMethodCallback callback) throws android.os.RemoteException;
/**
* 保存Activity对象.
*/
public void registerActivity() throws android.os.RemoteException;
}
IBinder : IBinder 是一个接口,代表了一种跨进程通信的能力,只要实现了这个接口,这个对象就能跨进程传输;
IInterface : IInterface 代表的就是 Server 进程对象具备什么样的能力,即能提供哪些方法,对应的就是 AIDL 文件中定义的接口;
Binder : Java 层的 Binder 类,代表的其实就是 Binder 本地对象。BinderProxy 类是 Binder 类的一个内部类,它代表远程进程的 Binder 对象的本地代理。这两个类都继承自 IBinder, 因而都具有跨进程传输的能力,实际在跨越进程时 Binder 驱动会自动完成这两个对象的转换。
Stub : 编写AIDL的时候,编译工具会生成一个名为 Stub 的静态内部类,这个类继承了 Binder, 说明它是一个 Binder 本地对象,它实现了 IInterface 接口,表明它具有 Server 承诺给 Client 的能力,Stub 是一个抽象类,具体的 IInterface 的相关实现需要开发者自己实现。
编译工具根据定义的.aidl文件生成的.java代码虽然难于阅读,但是Android之所以这样设计并不无道理,因为当有多个 AIDL 文件的时候把 INanoMethod、Stub、Proxy 放在同一个文件里能有效避免 Stub 和 Proxy 重名的问题。
二、手动编写代码来实现跨进程调用
一次跨进程通信必然会涉及到两个进程,在这个例子中 BookManagerService 作为服务端进程,提供服务;ClientActivity 作为客户端进程,使用 BookManagerService提供的服务。那么服务端进程具备什么样的能力?能为客户端提供什么样的服务呢?前面介绍过的 IInterface 代表的就是服务端进程具体什么样的能力,即提供了什么功能的接口。因此需要定义一个 BookManager 接口,BookManager 继承自 IInterface ,表明服务端具备什么样的能力:
/**
* 这个类用来定义服务端 BookManagerService 具备什么样的能力
*/
public interface BookManager extends IInterface {
//提供添加书籍接口
void addBook(Book book) throws RemoteException;
}
只定义服务端具备什么样的能力是不够的,既然是跨进程调用,那需要实现一个跨进程调用对象 Stub。Stub 继承自 Binder, 说明它是一个 Binder 本地对象;实现 IInterface 接口,表明具有 Server 承诺给 Client 的能力;Stub 是一个抽象类,具体的 IInterface 的相关实现需要调用方自己实现:
public abstract class Stub extends Binder implements BookManager { ... public static BookManager asInterface(IBinder binder) {
if (binder == null)
return null;
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof BookManager)
return (BookManager) iin; //如果client和server在同一进程,则返回本地对象
return new Proxy(binder); //如果client和server不在同一进程,则创建并返回代理对象
} ... @Override
/**
* code
:每个方法都有一个int
类型的数字(编号)用来区分
。*
data
: 传过来的数据,其中包含参数,以及类的描述。*
reply
:传回的数据,要写入是否发生了Exception
,以及返回值。*
flags
:该方法是否有返回值 ,0
表示有返回值。*/
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) { case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true; case TRANSAVTION_addBook:
data.enforceInterface(DESCRIPTOR);
Book arg0 = null;
if (data.readInt() != 0) {
arg0 = Book.CREATOR.createFromParcel(data);
}
this.addBook(arg0); //调用本地.java中实现的方法
reply.writeNoException();
return true; }
return super.onTransact(code, data, reply, flags);
}
...
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); //转换成对应编号
}
接下来就要实现这个代理类 Proxy 了,既然是代理类自然需要实现 BookManager 接口:
public class Proxy implements BookManager { ... public Proxy(IBinder remote) {
this.remote = remote;
} @Override
public void addBook(Book book) throws RemoteException { Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0); //调用Stub的onTransact方法中对应函数
replay.readException();
} finally {
replay.recycle();
data.recycle();
}
} ...
}
以上关于 Stub 类中重点介绍下 asInterface
和 onTransact
:
asInterface :
当 Client 端在创建和服务端的连接,调用 bindService 时需要创建一个 ServiceConnection 对象作为入参。在 ServiceConnection 的回调方法 onServiceConnected 中 会通过这个 asInterface(IBinder binder) 拿到 BookManager 对象,这个 IBinder 类型的入参 binder 是驱动传上来的,正如上代码中看到的一样,方法中会去调用 binder.queryLocalInterface() 去查找 Binder 本地对象,如果找到了就说明 Client 和 Server 在同一进程,那么这个 binder 本身就是 Binder 本地对象,可以直接使用。否则说明是 binder 是个远程对象,也就是 BinderProxy,因此需要创建一个代理对象 Proxy,通过这个代理对象来是实现远程访问。
onTransact
:
在 Proxy 中的 addBook() 方法中首先通过 Parcel 将数据序列化,然后调用 remote.transact()。正如前文所述 Proxy 是在 Stub 的 asInterface 中创建,能走到创建 Proxy 这一步就说明 Proxy 构造函数的入参是 BinderProxy,即这里的 remote 是个 BinderProxy 对象。最终通过一系列的函数调用,Client 进程通过系统调用陷入内核态,Client 进程中执行 addBook() 的线程挂起等待返回,驱动完成一系列的操作之后唤醒 Server 进程,调用 Server 进程本地对象的 onTransact(),最终又走到了 Stub 中的 onTransact() 中,onTransact() 根据函数编号调用相关函数,这里要提及一下,在跨进程调用的时候不会传递函数名而是传递编号来指明要调用哪个函数,如果提供给client端的aidl文件接口数量或者顺序和server端的声明不一样,则会导致函数调用错位的问题。
在client端绑定服务及使用接口:
......
/**
* 尝试与服务端建立连接
*/
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.android.aidl.server");
intent.setPackage("com.android.NanoServer");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBookManagerService = BookManager.Stub.asInterface(service); //获得代理对象
if (BookManagerService != null) {
mBound = true;
}
} @Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
}; ......
//成功绑定服务后便可调用服务端提供的接口
try {
if (mBookManagerService != null) {
mBookManagerService.addBook(book);
}
} catch (RemoteException e) {
e.printStackTrace();
}
注:
- 如果 Client 和 Server 在同一个进程,那么直接就是调用这个方法。
- 如果是远程调用,Client 想要调用 Server 的方法就需要通过 Binder 代理来完成,也就是上面的 Proxy。
-end-
Android : 跟我学Binder --- (2) AIDL分析及手动实现的更多相关文章
- Android : 跟我学Binder --- (4) 驱动情景分析
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- Android : 跟我学Binder --- (6) JAVA实现
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- Android : 跟我学Binder --- (5) C++实现
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- Android : 跟我学Binder --- (3) C程序示例
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
- AIDL 发生异常的原因 Android java.lang.SecurityException: Binder invocation to an incorrect interface
我建立了两个project.一个是activity 的 ,一个是service 的. 在进行两个project通信时,应该有以下几点注意: 1.在activity project中引入service ...
- Android系统--Binder系统具体框架分析(二)Binder驱动情景分析
Android系统--Binder系统具体框架分析(二)Binder驱动情景分析 1. Binder驱动情景分析 1.1 进程间通信三要素 源 目的:handle表示"服务",即向 ...
- android 进程间通信 messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯? android 消息机制 进程间 android 进程间 可以用 handler么 messenger 与 handler 机制 messenger 机制 是不是 就是 handler 机制 或 , 是不是就是 消息机制 android messenge
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha messenger 是什么 binder 跟 aidl 区别 intent 进程间 通讯 ...
- 写给 Android 应用工程师的 Binder 原理剖析
写给 Android 应用工程师的 Binder 原理剖析 一. 前言 这篇文章我酝酿了很久,参考了很多资料,读了很多源码,却依旧不敢下笔.生怕自己理解上还有偏差,对大家造成误解,贻笑大方.又怕自己理 ...
随机推荐
- phpcms网页替换验证码功能 及 搜索功能
在使用phpcms替换网页的时候,除了正常的替换栏目.内容页等,其他的什么验证码啦,提交表单了,搜索功能了,这些在替换的时候可能会对一些默认文件有一些小小 的改变 下面就是自己在失败中成功的过程,最后 ...
- phpcms栏目点击选中
点击选中(没有二级栏目) {pc:content action="category" catid="0" num="4" siteid=&q ...
- Shell脚本:while read line无法读取最后一行的问题
[1]Shell脚本:while read line无法读取最后一行的问题 刚刚利用shell脚本处理日志文件时,发现了一个问题:while read line无法读取到最后一行 通过编辑器可以看到待 ...
- 转:在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档
在 C# 中使用 P/Invoke 调用 Mupdf 函数库显示 PDF 文档 一直以来,我都想为 PDF 补丁丁添加一个 PDF 渲染引擎.可是,目前并没有可以在 .NET 框架上运行的免费 PDF ...
- 2440nandflash启动过程再学习
2011-02-13 12:27:05 2440nandflash启动,先是nandflash的前4K自动复制到CPU的0x0地址开始的4K区域. 然后CPU开始运行这4K(刚才copy过来的代码), ...
- samba服务器笔记 (一)
Samba安装 samba:主服务包:samba-client:客户端:samba-common:通用工具:samba4-libs:库:samba-winbind:windows域映射:samba-w ...
- Javascript 面向对象编程2:构造函数的继承
这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例.对象之间的"继承"的五种方法.比如,现在有一个"动物"对象 ...
- HTML5入门教程:响应式页面布局
摘自:https://www.sohu.com/a/225633935_647584 随着互联网时代的发展,我们对网页布局有了新的要求,大气,美观,能够在不同的设备上呈现令人焕然一新的效果.此时,一个 ...
- springmvc sessionfilter 登录过滤器
1.在web.xml中配置 <!-- sessionfilter --> <filter> <filter-name>sessionFilter</filte ...
- List添加map,后添加的map覆盖前面的问题
List resultList = new ArrayList(); Map map = new HashMap(); while(rs.next()){ String userid = rs.get ...