Android进程间通信(二):通过AIDL介绍Binder的工作机制
转载请以链接形式标明出处: 本文出自:103style的博客
《Android开发艺术探索》 学习记录
base on AndroidStudio 3.5.1
目录
- Binder介绍
- AIDL示例
- 小结
Binder介绍
- 直观来说,
Binder
是Android
中的一个类,它实现了IBinder
接口. - 从
IPC
上来说,Binder
是Android
实现进程间通信的一种1方式. - 从
Android Framework
角度来说,Binder
是ServiceManager
连接各种Manager(ActivityManager、WindowManager)
和相应的ManagerService
的桥梁. - 从应用层来说,
Binder
是 客户端和服务端通信的媒介.
Android开发中,Binder 主要用在 Service
中,包括 AIDL 和 Messenger,普通 Service 中的 Binder 不涉及进程间通信。
而 Messenger 底层也是基于 AIDL 的, 所以我们以 AIDL 来介绍 Binder 的工作机制。
AIDL示例
目录结构:
- 在
app/src/main
下创建aidl
的文件目录,然后创建包名aidl
,添加Book.aidl
和IBookManager.aidl
. - 在
app/src/main/java
目录下 创建包名aidl
,然后添加Book.java
文件。
Book.aidl
:
package aidl;
parcelable Book;
IBookManager.aidl
:
package aidl;
import aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Book.java
: 只要添加 bookId
,bookName
属性,然后实现 Parcelable
接口,然后按照 AS
的报红提示实现对应方法就好了。
package aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
public int bookId;
public String bookName;
//....
}
然后点击 菜单栏 的 Build
→ Make Project
.
然后切换到 Android
视图,在 java(generated) /aidl/
下会自动创建一个 IBookManager
的文件.
自动创建的 IBookManager
大致的结构如下,省略了部分内容:
package aidl;
public interface IBookManager extends android.os.IInterface {
public java.util.List<aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(aidl.Book book) throws android.os.RemoteException;
public static abstract class Stub extends android.os.Binder implements aidl.IBookManager {
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
private static final java.lang.String DESCRIPTOR = "aidl.IBookManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static aidl.IBookManager asInterface(android.os.IBinder obj) {
//...
}
public android.os.IBinder asBinder() {
return this;
}
public boolean onTransact(...) throws android.os.RemoteException {
//...
}
private static class Proxy implements aidl.IBookManager {
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 java.util.List<aidl.Book> getBookList() throws android.os.RemoteException {
//...
}
public void addBook(aidl.Book book) throws android.os.RemoteException {
//...
}
}
}
我们可以看到结构其实很简单,
首先声明了我们在 IBookManager.aidl
中 声明的两个方法 getBookList()
和 addBook(aidl.Book book)
;
然后在Stub
中 声明了两个整形的值用于标记这两个方法,用于在 onTransact
对应具体的方法;
Stub
类是一个 Binder
类,当客户端和服务端在同一进程,方法调用不会走 onTransact
,只有在不同进程才会走 onTransact
,这个逻辑是由 Stub
的内部类 Proxy
来完成的。
下面介绍 IBookManager
相关属性的含义:
DESCRIPTOR:
Binder的唯一标识,一般用类名表示,例如本例中的 aidl.IBookManager
。
asInterface(android.os.IBinder obj): 用于服务端的 Binder 对象转化成客户端所需的 AIDL 接口类型的对象,转换过程是区分进程的,同一进程返回 Stub 对象本身,不同进程返回 Stub.Proxy 对象。
asBinder(): 返回当前的Binder对象。
boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags):
运行在服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后的交由此方法来处理。服务端通过 code
来确定客户端请求的方法是哪个;从 data
中取出需要的参数;然后执行目标方法,将结果写入 reply
, 如果方法返回 false
, 客户端即请求失败,所以我们用这个特性来做权限验证。
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 TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
}
Proxy#getBookList()
运行在客户端,当客户端远程调用此方法:首先创建方法所需输入型 Parcel 对象 _data
,输入型 Parcel 对象 _reply
和返回对象 _result
,然后把方法的参数写入 _data
中,然后调用 transact
方法发起 RPC (远程过程调用)
请求,同时线程挂起,然后服务端的 onTransact(...)
被调用,知道 RPC 过程返回后,当前线程继续执行,并从 _reply
中获取返回结果,最后返回 _reply
中的数据。
public java.util.List<aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
Proxy#addBook
和 getBookList()
流程差不多,因为没有返回值,所以不需要从 _reply
中获取返回值。
public void addBook(aidl.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
接下来我们介绍两个很重要的方法 linkToDeath
和 unlinkToDeath
.
我们知道 Binder 运行在服务端进程,如果服务端意外终止,这时到服务端的连接就会断开,从而导致远程调用失败,从而导致客户端功能收到影响。
为了解决这个问题,Binder 给我们提供了 linkToDeath
和 unlinkToDeath
这两个配对方法。通过 linkToDeath 可以给 Binder 设置一个死亡代理,在意外终止的时候,代理就会收到通知,我们就可以重新发起连接请求从而恢复连接。
那如何设置这个代理呢? 首先,声明一个 DeathRecipient 对象。 DeathRecipient 是一个接口,其内部只有一个 binderDied 方法,当Binder死亡时,就会调用这个方法,我们就可以在这里 移出之前绑定的 binder代码,并重新绑定远程服务。客户端示例如下:
//binder意外终止的代理
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (remoteBookManager==null){
return;
}
remoteBookManager.asBinder().unlinkToDeath(deathRecipient,0);
remoteBookManager = null;
Intent intent = new Intent(BookManagerActivity.this, BookManagerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
};
//服务连接
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager iBookManager = IBookManager.Stub.asInterface(service);
try {
service.linkToDeath(deathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
...
}
};
通过以上的介绍我们大概了解了 Binder 的工作机制,不过还有两点要注意下:
- 因为客户端发起 RPC 会被挂起,所以 耗时较久 的操作不能在 UI线程 中发起。
- 由于 Binder 是 运行在服务端的 Binder 线程池中,所以 Binder 方法不管是否耗时都应 采用同步 的方式去实现。
Binder机制图 如下:
小结
本文我们主要通过一个 AIDL 的示例,通过 IDE 自动生成 IBookManager
, 然后分析了里面对应属性、方法和类,了解了Binder机制的工作流程。
下一节介绍 Android中实现进程间通信(IPC)的方式。
如果觉得不错的话,请帮忙点个赞呗。
以上
相关文章
- axios的简单使用
- IDEA整合Android
- 羊了个羊逆天改命过关法!PC小程序抓包教程
- AndroidQ兼容性适配指南
- AndroidR兼容性适配指南
- Android代码静态检查(lint、Checkstyle、ktlint、Detekt)
- 开发时遇到监听的事件处理机制和SoundPool播放音效解决方法以及外部类的使用【Android】
- 深入学习Android
- 基于Axios封装HTTP类库
- Android 3分钟带你入门开发测试
- Android 加载图片占用内存分析
- Android 原生 SQLite 数据库的一次封装实践
- Android 多语言动态更新方案探索
- 新主题基于Bootstrap3
- 技术实操| 自有App如何引入小游戏(Android篇)
- 在Android系统上运行frp
- 查询IOS可降级版本
- 微信小程序 Spdier - OfferShow 反编译逆向(一)
- Android 组件化最佳实践 ARetrofit 原理
- java商城小程序收藏