在这篇博客中将重点介绍一下Binder连接池的使用方法。
首先,说明一下Binder连接池出现的原因以及使用目的。为什么会有Binder连接池这个概念,这需要从AIDL说起,正常来说,服务端新建一个AIDL接口后,都会同时创建一个Service,这个Service对象用于返回AIDL接口的底层调用对象Binder给客户端,试想一下,如果有10个不同的AIDL接口,那是不是要创建10个不同的Service,这肯定是不太合理的,为了避免这种情况的出现,就有了Binder连接池的概念。Binder连接池的最大目的就是减少Service的数量,实现只要通过一个Service就可以管理所有不同的AIDL。关于Binder连接池的更多介绍,可以参考一下《Android开发艺术探索》中关于Binder连接池的讲解。
如何使用好Binder连接池,下面通过代码来介绍一下,我会分别创建服务端和客户端的代码,通过这个简单直接的demo,让读者能够直接抓住binder连接池的使用要领。
服务端代码:
对服务端来说,最重要的是提供一个能够返回相应Binder对象的queryBinder接口,这个接口可以根据不同的标志返回不同AIDL接口所对应的Binder对象。这个queryBinder接口是Binder连接池的精髓所在,客户端在绑定远程Service后,可以先通过不同的标志获取服务端中相应AIDL接口所对应的Binder对象,再通过这个Binder就可以获取到服务端的AIDL接口,进而使用服务端AIDL接口中的方法了。
在服务端新建两个简单的AIDL接口,ICalculate提供加减运算,IStringAppend用于字符串的拼接。
interface ICalculate {
int add(int a,int b);//加法
int sub(int a,int b);//减法
}
interface IStringAppend {
String append(String str1,String str2);//字符串拼接
}
为了能够返回上面两个接口对应的binder对象,服务端还需要新建一个AIDL接口提供根据不同标志返回相应binder对象的功能。
interface IBinderPool {
IBinder queryBinder(int binderCode);
}
下面创建两个类分别继承ICalculate接口和IStringAppend接口中的Stub类并实现Stub中的抽象方法:
public class CalculateImpl extends ICalculate.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a+b;
}
@Override
public int sub(int a, int b) throws RemoteException {
return a-b;
}
}
public class StringAppendImpl extends IStringAppend.Stub {
@Override
public String append(String str1, String str2) throws RemoteException {
return str1+str2;
}
}
创建一个Service并在Service的onBind方法中返回相应的binder对象,这里可以根据业务需求进行约定,比如当标志为0时返回ICalculate接口对应的binder对象,当标志为1时返回IStringAppend对应的Binder对象:
public class BinderPoolService extends Service {
private static final int BINDER_CALCULATE=0;
private static final int BINDER_STRING_APPEND=1;
private Binder binder= new IBinderPool.Stub() {
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder iBinder=null;
switch (binderCode){
case BINDER_CALCULATE:
iBinder=new CalculateImpl();
break;
case BINDER_STRING_APPEND:
iBinder=new StringAppendImpl();
break;
}
return iBinder;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
不要忘了在AndroidManefest中service配置Service,这里说明一下,由于我的aidl文件所在包名为com.aidl.binderpool,故action为com.aidl.binderpool.IBinderPool,即包名+类名,其实只要客户端能够正确绑定到服务端Service就OK了。
<service android:name=".BinderPoolService">
<intent-filter>
<action android:name="com.aidl.binderpool.IBinderPool"/>
</intent-filter>
</service>
至此服务端的代码就结束了。
服务端代码在AS中的目录如下:
客户端代码:
首先需要先将服务端中aidl包下所有的aidl文件连同目录拷贝至客户端代码,之后客户端做的工作就比较简单了,首先绑定服务端Service,然后通过Service返回的IBinder对象获取IBinderPool接口,通过queryBinder方法获取所需要的AIDL接口所对应的Binder对象,最后获取相应的AIDL接口并调用相应的方法即可,客户端的MianActivity如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG="BinderActivity";
private static final int BINDER_CALCULATE=0;
private static final int BINDER_STRING_APPEND=1;
IBinderPool mBinderPool;
ICalculate mCalculate;
IStringAppend mStringAppend;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent(IBinderPool.class.getName());
bindService(intent,conn,BIND_AUTO_CREATE);
}
private ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mBinderPool=IBinderPool.Stub.asInterface(iBinder);
try {
IBinder calculateBinder=mBinderPool.queryBinder(BINDER_CALCULATE);
mCalculate=ICalculate.Stub.asInterface(calculateBinder);
Log.i(TAG,"1+2="+mCalculate.add(1,2));
Log.i(TAG,"10-6="+mCalculate.sub(10,6));
IBinder stringAppendBinder=mBinderPool.queryBinder(BINDER_STRING_APPEND);
mStringAppend=IStringAppend.Stub.asInterface(stringAppendBinder);
String result=mStringAppend.append("hello","world");
Log.i(TAG,"\"hello\"与\"world\"拼接=="+result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Intent intent=new Intent(IBinderPool.class.getName());
bindService(intent,conn,BIND_AUTO_CREATE);
}
};
@Override
public void onDestroy(){
unbindService(conn);
super.onDestroy();
}
}
查看一下控制台输出,如下:
客户端代码目录如下: