前言
进程是操作系统分配资源的最小单位,进程的主要结构是PCB(Progress Control Block),其中就包括进程名,物理地址等。各个进程互不影响,因为有进程隔离技术。正是因为进程隔离技术,所以不同进程的资源并不共享,当A进程想去操作B进程的方法时,就需要通过进程间通信技术,也就是IPC技术来完成调用。
图片来源出自:https://zhuanlan.zhihu.com/p/35519585
Binder与传统IPC对比
共享内存:无需拷贝,但是需要借助到Memory Map来完成用户空间 和内核空间的映射,这就涉及到同步的问题且不安全。
Socket:需拷贝两次,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,传入效率低,开销大且不安全
Binder:只需拷贝一次,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,借助Memory Map完成接收方和内核空间的映射,而且Binder机制需要为每个APP分配UID,在通信时需要鉴别身份。
图片.png图片来源出自:https://zhuanlan.zhihu.com/p/35519585
流程分析
在这借助Aidl来分析整个流程。
private void bindService() {
Intent intent = new Intent();
//开启服务端,也就是另一个进程的服务
intent.setComponent(new ComponentName("com.lxxl.binder_service", "com.lxxl.binder_service.AidlService"));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: success");
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: success");
iMyAidlInterface = null;
}
};
熟悉Aidl的知道,这个ServiceConnection 就是在启动服务需要传递的参数。重点是asInterface这个方法做了啥
public static com.lxxl.binder_service.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.lxxl.binder_service.IMyAidlInterface))) {
return ((com.lxxl.binder_service.IMyAidlInterface)iin);
}
//返回了一个Proxy对象
return new com.lxxl.binder_service.IMyAidlInterface.Stub.Proxy(obj);
}
首先,给我们返回了一个Proxy对象,这个对象用来干啥的??我们接着往下看这个Proxy类。
private static class Proxy implements com.lxxl.binder_service.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
``````省略
//这个方法就是在声明Aidl时自定义的方法。
@Override
public void addPerson(com.lxxl.binder_service.Person person) throws android.os.RemoteException
{
//这就是要需要通过binder传递的数据
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
//注释①
boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addPerson(person);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
注释①:在调用一个方法时,要知道方法名和形参列表,形参列表通过_data来传递,方法名则需要传递String,不过由于服务端和客户端的AIDL文件一致,我只需要传递一个整形值,就知道是第几个方法了,效率更高。
在调用完注释①后,会调用Binder的onTransact()方法来完成数据的发送。
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
case TRANSACTION_addPerson://整型值,代表第几个方法。
{
data.enforceInterface(descriptor);
com.lxxl.binder_service.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.lxxl.binder_service.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
}
至此,数据已经到用户空间到内核空间了,服务端怎么接收数据呢?
public IBinder onBind(Intent intent) {
persons = new ArrayList<>();
Log.e("AidlService", "success onBind");
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
//来自客户端的数据
persons.add(person);
}
@Override
public List<Person> getPersonList() throws RemoteException {
return persons;
}
};
通过Stub.onTransact()来完成。
总结
建议还是不借助Aidl的情况下手写一遍加强对binder机制的理解。
网友评论