Binder、AIDL、远程服务结合学习记录
本篇文章为自己学习binder过程中的知识总结和疑惑的解答,此文不讲解具体的使用方法,只讲源码,有什么错误请批评指正,互相进步。
什么是binder
Binder主要作用是进程间通信,其他几种进程间通信方法有文件系统、socket、管道、Intents、ContentProviders、Messenger、Binder。
Android系统分为四层,从上往下分别是application应用层、Framework层、native层、驱动层,下图是盗的图:
Android中的应用层和服务层不在同一个进程,系统服务在单独的进程中,并且不同的应用分属不同的进程,保证每个进程可以单独的运行,实现应用层与系统层的隔离。
Binder是基于内存映射(mmap)实现,进程中的用户区域不可以和物理设备直接打交道,因此把磁盘上的数据读取到用户空间,需要经过两次拷贝:
第一次:从磁盘空间到内核空间
第二次:从内核空间到用户控件。
因此使用内存映射mmap建立物理介质和用户空间的映射可以减少拷贝次数,提高效率
Binder并非物理介质,因此Binder驱动使用mmap()的映射关系,并非建立在物理介质和用户控件之间,而是建立内核空间和数据缓存空间的映射。
完整的Binder通信过程:
1、Binder驱动在内核空间创建一个数据接收缓冲区
2、内核空间开辟一块内核缓冲区,建立起内核缓存区域和内核中数据接受缓存区之间的映射,同时建立内核数据接收缓冲区和用户进程空间地址的映射关系。
3、发送方进程通过系统调用把数据复制到内核缓存区,由内核缓存区和接收进程之间的映射,把数据发送到了接收进程的用户控件,这样便完成了一次进程间的通信。
(这个图片也是直接盗的图)
binder中的transact和onTransact的区别
Binder实现远程通信的基础是IBinder,这个接口,而transact是这个接口中的方法
/**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data to send to the target. Must not be null.
* If you are not sending any data, you must create an empty Parcel
* that is given here.
* @param reply Marshalled data to be received from the target. May be
* null if you are not interested in the return value.
* @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*/
public boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
而onTransact则是继承这个接口的Binder.java中的一个方法
/**
* Default implementation is a stub that returns false. You will want
* to override this to do the appropriate unmarshalling of transactions.
*
* <p>If you want to call this, call transact().
*/
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
transact作用是让你可以向远端的IBinder对象发出调用,第二个方法可以让你的远程对象可以相应接受到的调用,IBinder中的API均是同步执行,源码如下
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
transact直到对方的Binder.onTransact方法调用完成才返回,transact传递的数据是Parcel,Parcel是一种一般的缓冲区,除了数据外,还有一些描述内容的元数据,元数据用于管理IBinder对象的引用,这样就可以在缓冲区从一个进程到另一个进程时保存这些引用,就可以保证当一个IBinder被写入到Parcel并发送到另一个进程中,如果另一个进程把同一个IBinder的引用发挥到原来的进程,那么原来的进程就能接受到发出的那个IBinder的引用,这种机制使得IBinder和Binder 像唯一标识符一样在进程间管理。
Binder支持进程间的递归调用,进程执行自己的IBinder的transact()调用进程B 的Binder,进程B在其Binder.onTransact()中又用transact()向进程A 发起调用,那么进程A 在等待它发出调用返回的同时,还会用Binder.onTransact()响应进程B的transact。
Binder的作用让我们跨进程间的调用变成如同调用接口一般。
transact中的参数解析:
code:int型,这个代表这次通信的requestID,这样子在Binder端就可以根据requestID调用对应的代码了,可以理解为方法名
data:可以理解为方法中的参数
reply:方法的返回值
flag:这个flag代表Binder通信是否是同步还是异步,暂时可以忽略。我们重点关注三个。
什么是AIDL
aidl本质上是对Binder的封装
下面内容适合已经成功建立了AIDL通信,然后对着AIDL文件生成的java文件观看。
AIDL中的stub和proxy
首先看一下asInterface的源码,他是抽象静态类Stub中的一个静态方法,源码中的意思,就是如果这是同一个进程间的调用,那么就返回Stub,如果是远程调用,那么就返回Stub.proxy对象
/**
* Cast an IBinder object into ancs.dn.remote.AIDLRemoteInterface interface,
* generating a proxy if needed.
*/
public static cs.dn.remote.AIDLRemoteInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cs.dn.remote.AIDLRemoteInterface))) {
return ((cs.dn.remote.AIDLRemoteInterface)iin);
}
return new cs.dn.service.remote.AIDLRemoteInterface.Stub.Proxy(obj);
}
proxy是Stub中的一个静态内部类,下面中的onconnect是我的AIDL文件中的一个实现类,其中在proxy中的实现如下,onConnect方法中显示用Parcel的data往接口中写入DESCRIPTOR,然后调用transact方法,上面讲述binder的时候已经说过,transact作用是向远端进程发起调用,直到对方的Binder.onTransact方法调用完成才返回,这个过程是同步进行。
private static class Proxy implements cn.kuwo.service.remote.AIDLRemoteInterface
{
@Override public void onConnect() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_onConnect, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
远程服务与本地服务
AIDL一般结合service使用,首先看一下什么是远程服务
android:exported="true"
这一句话的作用标明此服务可以被其他的app吊起,service和Activity一样可以被其他的应用启动,(此处一直迷惑了很久才区分开远程和被其他进程调起不是一回事)
android:process=":remote"
这一句话表示该服务运行在独立的进程中。而对于service这个控件而言,还有一个神奇的地方,他可以在serviceConnect中返回一个Binder对象,不得不说,service空间在设计之初就考虑到这个问题。请看下面实例。
<service
android:name=".RemoteService"
android:enabled="true"
android:exported="true"
android:process=":remote">
<intent-filter android:priority="1000">
<action android:name="cs.dn..RemoteService"/>
</intent-filter>
</service>
<service
android:name=".MainService"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="cs.dn.MainService"/>
</intent-filter>
</service>
看一下service中的bindService的源码:
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
这个方法中第一个参数是一个intent,第二个是一个ServiceConnection,让我们看一下ServiceConnection的源码
/**
* Interface for monitoring the state of an application service. See
* {@link android.app.Service} and
* {@link Context#bindService Context.bindService()} for more information.
* <p>Like many callbacks from the system, the methods on this class are called
* from the main thread of your process.
*/
public interface ServiceConnection {
/**
* Called when a connection to the Service has been established, with
* the {@link android.os.IBinder} of the communication channel to the
* Service.
*
* @param name The concrete component name of the service that has
* been connected.
*
* @param service The IBinder of the Service's communication channel,
* which you can now make calls on.
*/
void onServiceConnected(ComponentName name, IBinder service);
/**
* Called when a connection to the Service has been lost. This typically
* happens when the process hosting the service has crashed or been killed.
* This does <em>not</em> remove the ServiceConnection itself -- this
* binding to the service will remain active, and you will receive a call
* to {@link #onServiceConnected} when the Service is next running.
*
* @param name The concrete component name of the service whose
* connection has been lost.
*/
void onServiceDisconnected(ComponentName name);
/**
* Called when the binding to this connection is dead. This means the
* interface will never receive another connection. The application will
* need to unbind and rebind the connection to activate it again. This may
* happen, for example, if the application hosting the service it is bound to
* has been updated.
*
* @param name The concrete component name of the service whose
* connection is dead.
*/
default void onBindingDied(ComponentName name) {
}
}
这是一个接口类,类中包含了三个回调,用来监听服务的连接状态,
void onServiceConnected(ComponentName name, IBinder service);
这个在服务器连接成功调用,注意他的第二个参数,正是一个IBinder,而这个binder也正是我们跨进程调用的重中之重。
远程服务与AIDL建立连接
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
LogUtils.log(TAG,"onServiceConnected","componentName: " + componentName.getClassName());
remoteInterface = AIDLRemoteInterface.Stub.asInterface(iBinder);
try {
remoteInterface.connect(PlayDelegateImp.getInstance());
}catch (RemoteException e){
LogMgr.e(TAG,e);
}
。。。。。。
super.onServiceConnected(componentName, iBinder);
}
上面是我自己代码中的一段伪代码,我们调用AIDLRemoteInterface.Stub.asInterface(iBinder)方法,获取AIDL的接口,从此所有接口中的调用都可以直接通过remoteInterface 来获取到,大家注意,这个里面获取到的binder正是我们自己定义的远程服务的IBinder对象。
至此,binder,AIDL和远程服务的结合使用已经简单的介绍完毕
(参考:
https://blog.youkuaiyun.com/u013309870/article/details/105328743
https://blog.youkuaiyun.com/sergeycao/article/details/52585411
https://www.jianshu.com/p/2228c6c67144
)