Binder概念
1.从机制的角度上说,Binder是一种Android中实现的跨进程通信(IPC)的方式。
2.从组成上,Binder是一种虚拟的物理设备驱动,存放在/dev/binder? 路径下面
3.从代码层角度,Binder是一个类,实现了IBinder接口(java层)
Binder的作用
1.在Android中实现跨进程通信。
2.连接Service进程,Client进程和ServiceManager进程。
3.将Binder机制模型以代码的形式实现在Android系统中。
为什么采取Binder通信机制呢?
我们先来看看传统的linux跨进程通信机制都有哪些
1.无名pipe和消息队列,这个采用的是存储转发的方式,至少需要拷贝两次才行,效率低
2.共享内存,传输过程中不需要拷贝,但是控制机制复杂(通信的过程中,需要获取对方的pid,需要用到多个机制才能实现)
3.socket,是一个通用的接口,导致其传输效率低,开销大
由于上面这些通信机制的弊端,Android对上面的机制进行借鉴和优化,研发出来一个新的机制Binder,这是一种C/S的通信模型,在内核中添加了身份标识PID和UID,安全性比较高,Binder还创建了私有管道,而Linux的访问接入点是开放的。
Binder通信的原理:
Binder机制进程间通信:是将数据写入驱动层这里会进行一次数据拷贝,这里是客户端完成的,在服务端需要创建一个服务,并进行注册,在注册的过程中会在驱动层建立映射关系,服务层跟驱动层可以直接相互获取数据,这里采用的是内存共享的方式进行。在交互的过程中,绑定了PID并通过方法ID的映射来进行调用,保证了安全性和更高的性能。
跨进程通信例子
下面借助AIDL来实现,AIDL是一个模板文件,协助生成一些重复的代码,创建一个MyAIDL文件
触发构建,Android Studio 自动生成一个MyAIDL java文件,如下图:
AIDL文件
interface MyAIDL {
void setAddress(String address);
String getAddress();
}
构建之后帮我们生成的AIDL java 文件,这里边重要的有两部分一个Stub一个是Proxt,其中Stub属于接收方,Proxy 属于发送方,下面把重点的内容进行解析,先来看Stub部分
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements MyAIDL {
private static final String DESCRIPTOR = "com.example.testbinder.MyAIDL";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.testbinder.MyAIDL interface,
* generating a proxy if needed.
*/
public static MyAIDL asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof MyAIDL))) {
return ((MyAIDL) iin);
}
return new Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_setAddress: {
data.enforceInterface(descriptor);
String _arg0;
_arg0 = data.readString();
this.setAddress(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getAddress: {
data.enforceInterface(descriptor);
String _result = this.getAddress();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
在Binder机制里边,必然包含Stub部分,这个Stub名称可以随意更改,但是必须要继承Binder并实现MyAIDL这个接口
?this.attachInterface(this, DESCRIPTOR); 构造方法使用全类名字符串进行绑定
?android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);这里先从本地查找,如果
?能找到说明不是跨进程通信,直接将本地的Binder返回,如果没有找到说明是跨进程,直接创建
?一个。
?onTransact 这个是接收方,当调用transact之后触发这个方法。
下面来看Proxy部分
private static class Proxy implements MyAIDL {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void setAddress(String address) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(address);
boolean _status = mRemote.transact(Stub.TRANSACTION_setAddress, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setAddress(address);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public String getAddress() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getAddress, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getAddress();
}
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static MyAIDL sDefaultImpl;
}
这个里边分别是setAddress和getAddress的实现,这里边实现类似,得到两个普通缓存区_data和_reply ,设置DIP,并执行transact方法,这个方法的第一个参数是方法映射,这里的调用为了效率更高不是传输的方法名,是传的方法整形映射。在另外一段也有同样的方法映射,这样就能找到需要调用的方法。
另外进程间通信,必须依托于服务,创建一个Service
public class IPCService extends Service {
public IPCService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new IPCBinder();
}
private class IPCBinder extends MyAIDL.Stub {
private String address;
@Override
public void setAddress(String address) throws RemoteException {
this.address = address;
}
@Override
public String getAddress() throws RemoteException {
return address;
}
}
}
?主意声明IPCBinder 继承的是MyAIDL.Stub,这里是服务的提供方,这里提供的是什么,在客户端在绑定的时候得到的就是什么,如果不能直接继承Binder
创建另外一个App,来作为客户端
public class MainActivity extends AppCompatActivity {
private MyAIDL mMyAIDL;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setAction("com.example.testbinder.IPCService");
intent.setPackage("com.example.testbinder");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyAIDL = MyAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
}
public void startIPC(View view) {
if (mMyAIDL != null) {
try {
mMyAIDL.setAddress("beijing");
Toast.makeText(this, "" + mMyAIDL.getAddress(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
这样便可以模拟IPC进程间通信了,注意一定要将AIDL部分拷贝过来。
总结:Binder就是通过DIP来标识,并通过调用方法映射进行方法调用,transact是将数据写到驱动(此时还处于客户端段进程),并触发onTransact方法(此时就到了远端进程)。transact将数据写到驱动,进行了一次数据拷贝,注册的服务跟驱动层有映射关系可以相互获取数据,这里是通过共享内存的方式来实现不需要数据拷贝。这样整个Binder的IPC过程只进行了一次数据拷贝,并使用了内存共享的方式来进行通信,相对于传统的Linux通信方式高效而且安全。 ?
|