本文介绍通过aidl实现跨进程的一些小技巧,比如Binder池、监听服务死亡和重新绑定、RemoteCallbackList用法等内容。
为了方便理解我们用代码来说明下,其中细节在代码备注中写得很清楚
非默认支持的类型aidl
// Preson.aidl
package com.example.fhl.aidl.inouts;
parcelable Preson; //Preson.aidl与Preson.java的包名要一样
/**
*默认生成的模板类的对象只支持为 in 的定向 tag
* 如果要支持为 out 或者 inout 的定向 tag 的话,
* 还需要实现 readFromParcel() 方法——而这个方法其实并没有在 Parcelable 接口里面
*/
public void readFromParcel(Parcel dest){
//注意,此处的读值顺序应当是和writeToParcel()方法中一致的
name = dest.readString();
age = dest.readInt();
}
为了让Preson.aidl与Preson.java的包名要一样有两种方式,第一种方式就是把Preson.java放到java目录相同包名下;第二种是把Preson.java文件放到aidl目录下的Preson.aidl相同包名下,AS要在gradle需要配置如下;
sourceSets{
main {
java.srcDirs = ["src/main/java", "src/main/aidl"]
}
}
定义方法的接口aidl
// IPresonManager.aidl
package com.example.fhl.aidl.inouts;
import com.example.fhl.aidl.inouts.Preson; //非默认数据类型需要导包,即使相同包下也要导包
// Declare any non-default types here with import statements
interface IPresonManager {
List<Preson> getPresons();
//非默认类型需要加上定向tag
Preson addPresonIn(in Preson preson);
Preson addPresonOut(out Preson preson);
Preson addPresonInOut(inout Preson preson);
}
服务端代码
public class InoutService extends Service {
private CopyOnWriteArrayList<Preson> list = new CopyOnWriteArrayList<>();
@Override
public IBinder onBind(Intent intent) {
return new PresonManager();
}
private class PresonManager extends IPresonManager.Stub{
@Override
public List<Preson> getPresons() throws RemoteException {
return list;
}
@Override
public Preson addPresonIn(Preson preson) throws RemoteException {
if (preson == null) {
Log.e("InoutService", "preson is null.");
preson = new Preson();
}
preson.setAge(28);
if (!list.contains(preson)) {
list.add(preson);
}
Log.e("InoutService", "list: "+list.toString());
return preson;
}
@Override
public Preson addPresonOut(Preson preson) throws RemoteException {
if (preson == null) {
Log.e("InoutService", "preson is null.");
preson = new Preson();
}
preson.setAge(28);
if (!list.contains(preson)) {
list.add(preson);
}
Log.e("InoutService", "list: "+list.toString());
return preson;
}
@Override
public Preson addPresonInOut(Preson preson) throws RemoteException {
if (preson == null) {
Log.e("InoutService", "preson is null.");
preson = new Preson();
}
preson.setAge(28);
if (!list.contains(preson)) {
list.add(preson);
}
Log.e("InoutService", "list: "+list.toString());
return preson;
}
}
}
我在开发中经常一个业务对应一个aidl接口,一个aidl对应一个service,如果有多个业务就有很多aidl接口,就需要service,这样服务端开销就会变大。如果我们把所有的aidl放入一个service中进行管理,这样就能避免开销大的问题,那有什么办法呢,接下来我讲下binder连接池来解决这个问题。
首先我们定义一个用来查找其他业务模块的aidl。
// IBinderPooll.aidl
package com.example.fhl.aidl;
// Declare any non-default types here with import statements
interface IBinderPooll {
IBinder queryBinder(int code); //根据code查询对应的业务模块binder
}
服务端代码:实现IBinderPooll, 通过实现的queryBinder格局code返回不同模块的ibinder
public class BinderPooll extends IBinderPooll.Stub{
private static final int MODEL_1 = 1;
private static final int MODEL_2 = 2;
@Override
public IBinder queryBinder(int code) throws RemoteException {
IBinder iBinder = null;
switch (code){ //根据客户端传进来的code返回对应的ibinder
case MODEL_1:
iBinder = new Mode1();
break;
case MODEL_2:
iBinder = new Mode2();
break;
default:
break;
}
return iBinder;
}
}
服务端service就简单了,在onBind中直接返回BinderPooll对象
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new BinderPooll();
}
}
客户端代码,首先建立一个BinderPooll 单例,在这个单例中通过同步方法connBinderService连接到服务端MyService并且拿到IBinderPooll对象,然后通过IBinderPooll的queryIBinder查找对应的ibinder
public class BinderPooll {
private volatile static BinderPooll mBinderPooll;
private IBinderPooll mIBinderPooll;
private Context mContext;
private CountDownLatch mCountDownLatch;//同步用
private BinderPooll(Context context) {
mContext = context.getApplicationContext();
connBinderService();
}
public static BinderPooll getInstance(Context context){ //双重检测单例
if (mBinderPooll == null) {
synchronized(BinderPooll.class){
if (mBinderPooll == null) {
mBinderPooll = new BinderPooll(context);
}
}
}
return mBinderPooll;
}
public IBinder queryIBinder(int code){
IBinder iBinder = null;
if (mIBinderPooll != null) {
iBinder = mBinderPooll.queryIBinder(code);
}
return iBinder;
}
private synchronized void connBinderService(){
mCountDownLatch = new CountDownLatch(1);
Intent intent = new Intent();
intent.setAction("com.example.fhl.aidl.servcie.easy.MyService");
intent.setPackage("com.example.fhl.aidl");
mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
try {
mCountDownLatch.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (mIBinderPooll == null) {
throw new RuntimeException("mIBinderPooll is null");
}
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mIBinderPooll = IBinderPooll.Stub.asInterface(iBinder);
try {
mIBinderPooll.asBinder().linkToDeath(deathRecipient, 0); //绑定死亡监听
} catch (RemoteException e) {
e.printStackTrace();
}
mCountDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mIBinderPooll = null;
}
};
//服务是否死亡监听
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mIBinderPooll.asBinder().unlinkToDeath(deathRecipient, 0); //取消死亡监听
mIBinderPooll = null;
//重新连接
connBinderService();
}
};
}
代码就重复贴了,跟上面代码一样,通过ibinder的linkToDeath方法绑定死亡监听IBinder.DeathRecipient,但服务死亡的时候会回调binderDied方法,然后可以在这个方法里面做重置和重连等操作。
多个Activity或者多个进程调用aidl传入回调接口的时候服务端只保留一个最近的一个回调接口,所有需要一个集合在服务端保持这些回调接口,RemoteCallbackList是专门用来保持服务端回调接口的集合。
public class RemoteCallBackListService extends Service {
private RemoteCallbackList<ILisntener> mRemoteCallBackList = new RemoteCallbackList<>();
@Override
public IBinder onBind(Intent intent) {
return new ManagerListener();
}
private class ManagerListener extends IManagerLisntener.Stub{
@Override
public void registerListener(ILisntener listener) throws RemoteException {
if (listener != null) {
mRemoteCallBackList.register(listener);
}
}
@Override
public void unregisterListener(ILisntener listener) throws RemoteException {
if (listener != null) {
mRemoteCallBackList.unregister(listener);
}
}
}
//给所有注册的监听回调
private void returnCallBack(){
int size = mRemoteCallBackList.beginBroadcast();
for (int i = 0; i < size; i++) {
ILisntener lisntener = mRemoteCallBackList.getBroadcastItem(i);
if (lisntener != null) {
try {
lisntener.onCallBack("hello");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mRemoteCallBackList.finishBroadcast();
}
@Override
public void onDestroy() {
if (mRemoteCallBackList != null) {
mRemoteCallBackList.kill();
}
super.onDestroy();
}
}
代码链接 (https://github.com/dragonfan/AIDL)