Binder是Android中的一个类,实现了IBinder接口。从IPC角度Binder是Android中的一种跨进程通信方式;从Android framework角度来说,Binder是servicemanager连接各种manager(activitymanager、windowmanager…)的桥梁;从Android应用层来说,Binder是客户端和服务器进行通信的媒介,当bindservice的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据;Android开发中,Binder主要用在service中,包括AIDL和messenger,其中messenger底层也是AIDL,所以本篇文章通过AIDL来讲解Binder工作机制;
首先在工程中新建一个aidl文件:
package com.aidl.test;
interface IMath{
int add(int a,int b);
int sub(int a,int b);
}
然后在gen目录系统会自动生成IMath.java文件,这就是我们要分析的重点文件:
package com.aidl.test;
public interface IMath extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
//Stub()就是一个binder类
public static abstract class Stub extends android.os.Binder implements com.aidl.test.IMath {
private static final java.lang.String DESCRIPTOR = "com.aidl.test.IMath";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.aidl.test.IMath interface,
* generating a proxy if needed.
* 将服务器端的binder对象转换成客户端所需要的aidl接口类型的对象
*/
public static com.aidl.test.IMath asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//同进程返回本身
if (((iin != null) && (iin instanceof com.aidl.test.IMath))) {
return ((com.aidl.test.IMath) iin);
}
//不同进程,返回stub.Proxy对象
return new com.aidl.test.IMath.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
//onTransact运行在服务器端的binder线程池中,处理客户端发送的跨进程请求
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
//确定客户端发送的哪种请求,从data中取出目标方法所需的参数,当方法执行完之后,想reply写入返回值
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_sub: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sub(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//运行在客户端
private static class Proxy implements com.aidl.test.IMath {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public int add(int a, int b) throws android.os.RemoteException {
//创建输入和输出参数和返回值
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
//写入参数
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
//调用transact发起请求,此时当前线程被挂起,服务端的ontransact方法被调用
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
//写入返回值
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int sub(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//标明在transact过程中客户端所请求的是哪个方法
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
//aidl文件中声明的方法
public int add(int a, int b) throws android.os.RemoteException;
public int sub(int a, int b) throws android.os.RemoteException;
}
代码中已经写了相应的注释了,就不啰嗦了,需要注意的是客户端发起请求后,当前线程会被挂起,直至服务端进程返回数据,如果一个远程方法是很耗时的,就不能在主线程发起此远程请求,由于服务端的binder方法运行在binder的线程池中,所以binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。
Binder工作机制到此就差不多了,接下来看一下binder连接池:
binder连接池
先回顾一下AIDL的使用流程:首先创建一个service和一个AIDL接口,接着创建一个类继承AIDL的stub类,并实现其抽象方法,在onBind方法中返回这个类的对象,最后客户端通过bindservice绑定服务端的service,建立连接后就可以访问远程服务端的方法了;
import com.aidl.test.IMath.Stub;
public class MyService extends Service{
private Stub stub = new Stub() {
@Override
public int sub(int a, int b) throws RemoteException {
return a - b;
}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return stub;
}
}
public class MainActivity extends Activity implements OnClickListener {
private View mBind,mUnbind,mAdd,mSub;
private IMath math;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
math = IMath.Stub.asInterface(service);
Toast.makeText(getApplicationContext(), "绑定成功", 0).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBind = findViewById(R.id.bind);
mUnbind = findViewById(R.id.unbind);
mAdd = findViewById(R.id.add);
mSub = findViewById(R.id.sub);
mBind.setOnClickListener(this);
mUnbind.setOnClickListener(this);
mAdd.setOnClickListener(this);
mSub.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v == mBind){//绑定服务
Intent service = new Intent("com.aidl.test");
bindService(service , conn , BIND_AUTO_CREATE);
}else if(v == mUnbind){
unbindService(conn);//解绑
}else if(v == mAdd){
try {
int addResult = math.add(11, 99);
Toast.makeText(getApplicationContext(), addResult+"", 0).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}else if(v == mSub){
try {
int subResult = math.sub(99, 11);
Toast.makeText(getApplicationContext(), subResult+"", 0).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
当项目中有很多业务需要使用aidl来进行跨进程通信的时候,我们就按照基本流程一个一个实现吗?方法肯定是可以的,但是我们会创建很多service,势必会占用很多的资源,针对这个问题,我们要减少service的数量,将所有的aidl放在一个service中去管理,即binder连接池:
每个业务模块创建创建自己的aidl接口,然后向服务端提供自己的唯一标识和其对应的binder对象,对于服务端,只需要一个service就可以了,服务端提供一个querybinder接口,这个接口能够根据业务模块的特征来返回相应的binder对象,不同的业务模块拿到所需要的binder对象后就可以进行远程方法的调用了。