IPC之Binder连接池机制
一.前言
由于最近负责小度在家平台接入咪咕视频,因为咪咕视频是第三方的app,这样就涉及小度query指令透传到咪咕,咪咕的UIControl需要同步给小度云端的跨进程互相调用的场景。然后我看项目里又接入了爱奇艺,CIBN等一系列的第三方app,都是通过AIDL方式跨进程调用的,查看项目发现每个app对应一个Serivce,假如接入30个第三方app,按照项目现有的实现方式就会有30个Service,我们知道Service是系统组件,势必导致系统开销。
我们项目提供给第三方的sdk基本是AIDL方式提供接口的,AIDL应该很简单,这里我们回顾下AIDL的大概流程:新建一个Serivce和一个AIDL接口,然后继承AIDL接口的Stub并实现抽象方法,接着在Service的onBind方法中返回AIDL接口实例,接着客户端绑定Serivce并将Service#onBind方法IBinder实例传给ServiceConnection#onServiceConnected中,通过asInterface转成得到接口实例,这样客户端就可以调用服务端的接口方法了。
二.优化方案
基于上面提出的问题,主要是解决Service数量多的问题,同时每个模块有自己的AIDL实现接口并且通过BinderPool管理一个AIDL接口用于查询各模块的AIDL的IBinder实例,BinderPool对应仅仅一个Service,在Service中返回用于获取Binder连接池里的AIDL接口实例,通过这种机制可以用1个Service、多个AIDL接口的SDK方式对应几乎所有第三方的app接入。我们只要在Service#onBind的AIDL接口区分不同的标识,通过不同的标识返回对应模块的AIDL接口实例,我们就可以调用到各自模块的接口。
Binder池实现
1.新建二个AIDL接口
IMyAidlInterface(获取一个字符串)如下:
// IMyAidlInterface.aidl
package zw.chowen.binderpool;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String getValue();
}
IMyAidlInterface2(获取两个整形相加和)如下:
package zw.chowen.binderpool;
// Declare any non-default types here with import statements
interface IMyAidlInterface2 {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int add(int a, int b);
}
以上两个AIDL接口对应不同的进程。
2.新建AIDL池接口:用于获取相应模块的的IBinder接口。
IBinderPoolInterface:
// IBinderPoolInterface.aidl
package zw.chowen.binderpool;
// Declare any non-default types here with import statements
interface IBinderPoolInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
IBinder queryInterface(int type);
}
3.分别实现这三个AIDL接口
public class IMyAidlInterfaceImpl extends zw.chowen.binderpool.IMyAidlInterface.Stub {
@Override
public String getValue() throws RemoteException {
return "chowen#this is IMyAidlInterface";
}
}
public class IMyAidlInterfaceImpl2 extends IMyAidlInterface2.Stub {
@Override
public int add(int a, int b) throws RemoteException {
LOGGER.info("chowen#a and b >>" + (a + b));
return a+b;
}
}
public class BinderPoolServiceImpl extends IBinderPoolInterface.Stub {
public BinderPoolServiceImpl() {
super();
}
@Override
public IBinder queryInterface(int type) throws RemoteException {
IBinder binder = null;
switch (type) {
case 1:
binder = new IMyAidlInterfaceImpl();
break;
case 2:
binder = new IMyAidlInterfaceImpl2();
break;
}
return binder;
}
}
分别继承Stub并实现各自抽象方法,通过IBinderPoolInterface的实现类BinderPoolServiceImpl中queryInterface方法即可返回各自模块对应类型的IBinder对象。
4.创建Service及IBinder池机制
Service
/**
* Created by zhouwen on 2019-10-06 22:30
*/
public class BinderPoolService extends Service {
private static Logger LOGGER = Logger.getLogger("BinderPoolService");
@Nullable
@Override
public IBinder onBind(Intent intent) {
LOGGER.info("chowen#onBind=" + (IBinder) BinderPoolManager.getIns(this).getBinder());
//返回获取各模块IBinder的AIDL接口
return (IBinder) BinderPoolManager.getIns(this).getBinder();
}
@Override
public void onCreate() {
LOGGER.info("chowen#onCreate");
LOGGER.severe("chowen#process=" + Process.myPid());
super.onCreate();
}
@Override
public void onDestroy() {
LOGGER.info("chowen#onDestroy");
super.onDestroy();
BinderPoolManager.getIns(this).unbindService(this);
}
}
通过BinderPoolManager绑定BinderPoolService这个服务,然后在BinderPoolService#onBind方法返回IBinderPoolInterface实例。
IBinder池机制:
/**
* Created by zhouwen on 2019-10-06 22:24
* Binder连接池实例
*/
public class BinderPoolManager {
private static Logger LOGGER = Logger.getLogger("BinderPoolManager");
private static BinderPoolManager sBinderPoolManager;
private IBinderPoolInterface mIBinderPoolInterface;
public BinderPoolManager(Context context) {
bindService(context.getApplicationContext());
}
public static BinderPoolManager getIns(Context context) {
if (sBinderPoolManager == null) {
synchronized (BinderPoolManager.class) {
if (sBinderPoolManager == null) {
sBinderPoolManager = new BinderPoolManager(context);
}
}
}
return sBinderPoolManager;
}
public class IMyAidlInterfaceImpl extends zw.chowen.binderpool.IMyAidlInterface.Stub {
@Override
public String getValue() throws RemoteException {
return "chowen#this is IMyAidlInterface";
}
}
public class IMyAidlInterfaceImpl2 extends IMyAidlInterface2.Stub {
@Override
public int add(int a, int b) throws RemoteException {
LOGGER.info("chowen#a and b >>" + (a + b));
return a+b;
}
}
public IBinder queryInterface(int type) {
try {
LOGGER.info("chowen#queryInterface#mIBinderPoolInterface>>" + mIBinderPoolInterface);
if (mIBinderPoolInterface != null) {
return mIBinderPoolInterface.queryInterface(type);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
public class BinderPoolServiceImpl extends IBinderPoolInterface.Stub {
public BinderPoolServiceImpl() {
super();
}
@Override
public IBinder queryInterface(int type) throws RemoteException {
IBinder binder = null;
switch (type) {
case 1:
binder = new IMyAidlInterfaceImpl();
break;
case 2:
binder = new IMyAidlInterfaceImpl2();
break;
}
return binder;
}
}
public synchronized void bindService(Context context) {
Intent intent = new Intent(context, BinderPoolService.class);
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
public IBinderPoolInterface getBinder() {
return new BinderPoolServiceImpl();
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIBinderPoolInterface = IBinderPoolInterface.Stub.asInterface(service);
LOGGER.info("chowen#onServiceConnected#mIBinderPoolInterface="+ mIBinderPoolInterface);
}
@Override
public void onServiceDisconnected(ComponentName name) {
LOGGER.info("chowen#onServiceDisconnected");
}
};
public void unbindService(Context context){
context.unbindService(serviceConnection);
}
}
5.模拟多进程实现
我们在AndroidManifest文件中声明多个进程,这里就随便申请如下:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:process=":other" />
<service
android:name=".BinderPoolService"
android:process=":binder_pool" />
</application>
我们通过主进程,other进程分别调用相应的接口方法。
主进程:
IBinder binder = mBinderPoolManager.queryInterface(1);
IMyAidlInterface iMyAidlInterface1 = BinderPoolManager.IMyAidlInterfaceImpl.asInterface(binder);
LOGGER.info("chowen#onResume#iMyAidlInterface1=" + iMyAidlInterface1);
if (iMyAidlInterface1 != null) {
try {
LOGGER.info("chowen#onResume#value=" + iMyAidlInterface1.getValue());
} catch (RemoteException e) {
e.printStackTrace();
}
}
other进程:
IBinder binder = mBinderPoolManager.queryInterface(2);
IMyAidlInterface2 iMyAidlInterface2 = BinderPoolManager.IMyAidlInterfaceImpl2.asInterface(binder);
LOGGER.info("chowen#iMyAidlInterfaceImpl2=" + iMyAidlInterface2);
if (iMyAidlInterface2 != null) {
try {
LOGGER.info("chowen#a and b is>>>" + iMyAidlInterface2.add(10, 20));
} catch (RemoteException e) {
e.printStackTrace();
}
}
分别通过BinderPoolManager#queryInterface方法通过各自的标识类型,这里用1,2表示,去分别获取IBinderPoolInterface接口实现中对应的IBinder实例。
6.IBinder池执行效果:
2019-10-07 00:03:04.106 16689-16689/? E/MainActivity: chowen#process=16689
2019-10-07 00:03:04.185 16725-16725/? I/BinderPoolService: chowen#onCreate
2019-10-07 00:03:04.186 16725-16725/? E/BinderPoolService: chowen#process=16725
2019-10-07 00:03:04.189 16725-16725/? I/BinderPoolService: chowen#onBind=zw.chowen.binderpool.BinderPoolManager$BinderPoolServiceImpl@c1109bc
2019-10-07 00:03:04.191 16725-16725/? I/BinderPoolManager: chowen#onServiceConnected#mIBinderPoolInterface=zw.chowen.binderpool.BinderPoolManager$BinderPoolServiceImpl@7a09d45
2019-10-07 00:03:04.312 16689-16689/? I/BinderPoolManager: chowen#onServiceConnected#mIBinderPoolInterface=zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@4283e77
2019-10-07 00:03:10.127 16689-16689/? I/BinderPoolManager: chowen#queryInterface#mIBinderPoolInterface>>zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@4283e77
2019-10-07 00:03:10.132 16689-16689/? I/MainActivity: chowen#onResume#iMyAidlInterface1=zw.chowen.binderpool.IMyAidlInterface$Stub$Proxy@2a9f150
2019-10-07 00:03:10.134 16689-16689/? I/MainActivity: chowen#onResume#value=chowen#this is IMyAidlInterface
2019-10-07 00:03:13.119 16761-16761/? E/SecondActivity: chowen#process=16761
2019-10-07 00:03:13.140 16761-16761/? I/BinderPoolManager: chowen#onServiceConnected#mIBinderPoolInterface=zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@b36e1c0
2019-10-07 00:03:17.126 16761-16761/? I/BinderPoolManager: chowen#queryInterface#mIBinderPoolInterface>>zw.chowen.binderpool.IBinderPoolInterface$Stub$Proxy@b36e1c0
2019-10-07 00:03:17.128 16761-16761/? I/SecondActivity: chowen#iMyAidlInterfaceImpl2=zw.chowen.binderpool.IMyAidlInterface2$Stub$Proxy@2c1fa84
2019-10-07 00:03:17.130 16725-16740/? I/BinderPoolManager: chowen#a and b >>30
2019-10-07 00:03:17.130 16761-16761/? I/SecondActivity: chowen#a and b is>>>30
我们从Log看出分别处于不同两个进程,通过自己的AIDL接口分别打印出预期结果,并且Bind一个Service。
三.总结
通过Binder连接池机制可以很好的解决项目中多个Service的问题,降低系统创建Service的开销。通过一个AIDL接口对外提供获取相应模块的AIDL的接口实例。
步骤如下:
1.创建各自的AIDL文件并实现对应接口。
2.创建提供获取AIDL接口的AIDL文件。
3.创建AIDL连接池,主要用于启动服务,生成IBinder对象等。
4.创建1个Service,通过AIDL连接池获取对外的AIDL接口实例在onBind方法中返回。
流程如下:
image.pngDemo实现:binderpool