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.csdn.net/u013309870/article/details/105328743
https://blog.csdn.net/sergeycao/article/details/52585411
https://www.jianshu.com/p/2228c6c67144

猜你喜欢

转载自blog.csdn.net/u011976443/article/details/114898888