1、Binder基本概念
系统内部有一个Binder服务,相当于DNS服务器,用于分发客户端请求,并将请求发送到服务端处理,再将结果返回客户端。
2、为什么Binder进行IPC通讯会高效
普通IPC方式需要经过序列化、反序列化,会有两次内存拷贝,Binder机制内部有共享内存的概念,做一次内存拷贝即可
3、实现方式
(1)AIDL方式
注意点:AIDL文件在Server端创建,要讲生成的 IBinderXXX.java、数据实体类 文件复制到客户端
(2)自行实现IBinderXXX源码
两步,第一步定义IInterface接口
public interface IBookManager extends IInterface {
static final String DESCRIPTOR = "com.ideacode.study.binderserver.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);
public List getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
第二步编写Binder实现类,Proxy用于提供给客户端使用
public class BookManagerImpl extends Binder implements IBookManager {
public BookManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if (obj ==null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && (iin instanceof IBookManager)) {
return (IBookManager)iin;
}
return new Proxy(obj);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_getBookList:
data.enforceInterface(DESCRIPTOR);
List result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSACTION_addBook:
data.enforceInterface(DESCRIPTOR);
Book arg0;
if (0 != data.readInt()) {
arg0 = Book.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
this.addBook(arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public List getBookList() throws RemoteException {
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public List getBookList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List result;
try {
data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Book.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(TRANSACTION_addBook, data, reply, 0);
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
}
@Override
public IBinder asBinder() {
return this;
}
}
4、线程调度
客户端在主线程同步执行,服务器端在Binder线程响应请求
5、服务器端断开后自动重连
在客户端设置DeathRecipient连接监听,监听到服务器端断开后,再次执行重连操作。或者服务器端自定监听自己的服务是否死亡,自行维护服务重启。
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.e(TAG, "服务器连接已死亡");
if (bookManager == null) {
return;
}
bookManager.asBinder().unlinkToDeath(deathRecipient, 0);
bookManager = null;
startToBindService();
}
};
6、一些坑
(1)客户端bindService绑定服务器端服务失败
Android 11 后,要在客户端MainiFest中设置
服务端服务要设置action才能被客户端搜索到
客户端设置
private void startToBindService() {
Intent intent = new Intent();
intent.setAction("com.ideacode.study.binderserver.service.BookManagerCustomBinderService");
intent.setPackage("com.ideacode.study.binderserver");
bindService(intent, connection, Service.BIND_AUTO_CREATE);
}
服务器端设置
(2)实测 一加9 Android 12 真机,服务端APP未启动的情况下,客户端APP无法启动服务端APP的服务。模拟器正常