跳转至

Binder机制05-AIDL

AIDL简介

什么是AIDL

AIDL : Android Interface Definition Language,即Android接口定义语言。

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。

AIDL基本语法

AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。

其中对于Java编程语言的基本数据类型 (int, long, char, boolean,String,CharSequence)集合接口类型List和Map,不需要import 语句。而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。AIDL允许传递实现Parcelable接口的类,需要import。

需要特别注意的是,对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout。 in:client端将参数设定好,传给server,server端拷贝、使用。 out:client将接口、地址等“空壳”传给server,server将运算结果填充进去,然后client从中取出运算结果。 inout:client端既从此处传入数据,又从此处取出server填充的结果。

将完成in功能的设成out、或out功能的设成in,都会报错。若将连个均设成inout,则通过,但增加了序列化的步骤,白白浪费运算资源。

aidl文件

// IPerformanceService.aidl  
package com.rtfsc.aidldemo;  

interface IPerformanceService {  
    oneway void setProcessPriority(int pid, int priority);  
    void setThreadPriority(int tid, int priority);  
    int getThreadPriority(int pid);  
    void copyArrayIn(in String[] source);  
    void copyArrayOut(out String[] source);  
    void copyArrayInOut(inout String[] source);  
}

然后用编译工具编译之后,可以得到对应的 IPerformanceService.java 类,看看系统给我们生成的代码:

Stub类

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.rtfsc.i007service;

public interface IPerformanceService extends android.os.IInterface {
    /**
     * Default implementation for IPerformanceService.
     */
    public static class Default implements com.rtfsc.i007service.IPerformanceService {
        @Override
        public void setProcessPriority(int pid, int priority) throws android.os.RemoteException {
        }

        @Override
        public void setThreadPriority(int tid, int priority) throws android.os.RemoteException {
        }

        @Override
        public int getThreadPriority(int pid) throws android.os.RemoteException {
            return 0;
        }

        @Override
        public void copyArrayIn(java.lang.String[] source) throws android.os.RemoteException {
        }

        @Override
        public void copyArrayOut(java.lang.String[] source) throws android.os.RemoteException {
        }

        @Override
        public void copyArrayInOut(java.lang.String[] source) throws android.os.RemoteException {
        }

        @Override
        public android.os.IBinder asBinder() {
            return null;
        }
    }

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.rtfsc.i007service.IPerformanceService {
        private static final java.lang.String DESCRIPTOR = "com.rtfsc.i007service.IPerformanceService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.rtfsc.i007service.IPerformanceService interface,
         * generating a proxy if needed.
         */
        public static com.rtfsc.i007service.IPerformanceService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.rtfsc.i007service.IPerformanceService))) {
                return ((com.rtfsc.i007service.IPerformanceService) iin);
            }
            return new com.rtfsc.i007service.IPerformanceService.Stub.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_setProcessPriority: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.setProcessPriority(_arg0, _arg1);
                    return true;
                }
                case TRANSACTION_setThreadPriority: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.setThreadPriority(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getThreadPriority: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _result = this.getThreadPriority(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_copyArrayIn: {
                    data.enforceInterface(descriptor);
                    java.lang.String[] _arg0;
                    _arg0 = data.createStringArray();
                    this.copyArrayIn(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_copyArrayOut: {
                    data.enforceInterface(descriptor);
                    java.lang.String[] _arg0;
                    int _arg0_length = data.readInt();
                    if ((_arg0_length < 0)) {
                        _arg0 = null;
                    } else {
                        _arg0 = new java.lang.String[_arg0_length];
                    }
                    this.copyArrayOut(_arg0);
                    reply.writeNoException();
                    reply.writeStringArray(_arg0);
                    return true;
                }
                case TRANSACTION_copyArrayInOut: {
                    data.enforceInterface(descriptor);
                    java.lang.String[] _arg0;
                    _arg0 = data.createStringArray();
                    this.copyArrayInOut(_arg0);
                    reply.writeNoException();
                    reply.writeStringArray(_arg0);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.rtfsc.i007service.IPerformanceService {
            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 void setProcessPriority(int pid, int priority) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(pid);
                    _data.writeInt(priority);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_setProcessPriority, _data, null, android.os.IBinder.FLAG_ONEWAY);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().setProcessPriority(pid, priority);
                        return;
                    }
                } finally {
                    _data.recycle();
                }
            }

            @Override
            public void setThreadPriority(int tid, int priority) 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.writeInt(tid);
                    _data.writeInt(priority);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_setThreadPriority, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().setThreadPriority(tid, priority);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public int getThreadPriority(int pid) 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(pid);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getThreadPriority, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getThreadPriority(pid);
                    }
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void copyArrayIn(java.lang.String[] source) 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.writeStringArray(source);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_copyArrayIn, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().copyArrayIn(source);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void copyArrayOut(java.lang.String[] source) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((source == null)) {
                        _data.writeInt(-1);
                    } else {
                        _data.writeInt(source.length);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_copyArrayOut, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().copyArrayOut(source);
                        return;
                    }
                    _reply.readException();
                    _reply.readStringArray(source);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void copyArrayInOut(java.lang.String[] source) 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.writeStringArray(source);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_copyArrayInOut, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().copyArrayInOut(source);
                        return;
                    }
                    _reply.readException();
                    _reply.readStringArray(source);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            public static com.rtfsc.i007service.IPerformanceService sDefaultImpl;
        }

        static final int TRANSACTION_setProcessPriority = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_setThreadPriority = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getThreadPriority = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_copyArrayIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_copyArrayOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
        static final int TRANSACTION_copyArrayInOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);

        public static boolean setDefaultImpl(com.rtfsc.i007service.IPerformanceService impl) {
            // Only one user of this interface can use this function
            // at a time. This is a heuristic to detect if two different
            // users in the same process use this function.
            if (Stub.Proxy.sDefaultImpl != null) {
                throw new IllegalStateException("setDefaultImpl() called twice");
            }
            if (impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.rtfsc.i007service.IPerformanceService getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    public void setProcessPriority(int pid, int priority) throws android.os.RemoteException;

    public void setThreadPriority(int tid, int priority) throws android.os.RemoteException;

    public int getThreadPriority(int pid) throws android.os.RemoteException;

    public void copyArrayIn(java.lang.String[] source) throws android.os.RemoteException;

    public void copyArrayOut(java.lang.String[] source) throws android.os.RemoteException;

    public void copyArrayInOut(java.lang.String[] source) throws android.os.RemoteException;
}

系统帮我们生成了这个文件之后,我们只需要继承 IPerformanceService.Stub 这个抽象类,实现它的方法,然后在Service 的onBind方法里面返回就实现了AIDL。

这个Stub类非常重要,具体看看它做了什么。

Stub类继承自Binder

public static abstract class Stub extends android.os.Binder implements com.rtfsc.i007service.IPerformanceService {
    ...
}

Stub类继承自Binder,意味着这个Stub其实自己是一个Binder本地对象。

Stub实现 IPerformanceService 接口

public static abstract class Stub extends android.os.Binder implements com.rtfsc.i007service.IPerformanceService {
    ...
}

IInterface

public interface IPerformanceService extends android.os.IInterface {
    ...
}

IPerformanceService 本身是一个IInterface。

因此他携带某种客户端需要的能力(这里是方法 setProcessPriority等等 )。此类有一个内部类Proxy,也就是Binder代理对象。

asInterface

/**  
 * Cast an IBinder object into an com.rtfsc.i007service.IPerformanceService interface, * generating a proxy if needed. */public static com.rtfsc.i007service.IPerformanceService asInterface(android.os.IBinder obj) {  
    if ((obj == null)) {  
        return null;  
    }  
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
    if (((iin != null) && (iin instanceof com.rtfsc.i007service.IPerformanceService))) {  
        return ((com.rtfsc.i007service.IPerformanceService) iin);  
    }  
    return new com.rtfsc.i007service.IPerformanceService.Stub.Proxy(obj);  
}

看看 asInterface 方法,我们在bind一个Service之后,在onServiceConnecttion的回调里面,就是通过这个方法拿到一个远程的service的。

函数的入参obj是 IBinder 类型。 如果是Binder本地对象,那么它就是 Binder 类型; 如果是Binder代理对象,那就是 BinderProxy 类型。

正如上面自动生成的文档所说,它会试着查找Binder本地对象,如果找到,说明Client和Server都在同一个进程,这个参数直接就是本地对象,直接强制类型转换然后返回。

如果找不到,说明是远程对象(处于另外一个进程)那么就需要创建一个Binde代理对象,让这个Binder代理实现对于远程对象的访问。一般来说,如果是与一个远程Service对象进行通信,那么这里返回的一定是一个Binder代理对象,这个IBinder参数的类型是BinderProxy。

打开写demo调试的时候可以指定service在另一个进程里,观察asInterface方法用的是 Binder 还是 BinderProxy 可以在Androidmanifests文件中Service配置android:process=":service"

Proxy

再看看我们对于aidl的 getThreadPriority 方法的实现;在Stub类里面, getThreadPriority 是一个抽象方法,我们需要继承这个类并实现它。

如果Client和Server在同一个进程,那么直接就是调用这个方法;如果是远程调用,是通过Binder代理完成的,在这个例子里面就是 Proxy 类; Proxy 对于 getThreadPriority 方法的实现如下:

@Override  
public int getThreadPriority(int pid) 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(pid);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_getThreadPriority, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            return getDefaultImpl().getThreadPriority(pid);  
        }  
        _reply.readException();  
        _result = _reply.readInt();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
    return _result;  
}

它首先用 Parcel 把数据序列化了,然后调用了 transact 方法。

transact

这个 transact 到底做了什么呢?这个 Proxy 类在 asInterface 方法里面被创建,如果是Binder代理那么说明驱动返回的IBinder实际是 BinderProxy ,因此我们的 Proxy 类里面的 mRemote 实际类型应该是 BinderProxy ;我们看看 BinderProxy 的 transact 方法:(Binder.java的内部类)

frameworks/base/core/java/android/os/IBinder.java

/**  
 * 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.  
 * * @return Returns the result from {@link Binder#onTransact}.  A successful call  
 * generally returns true; false generally means the transaction code was not
 * understood.  For a oneway call to a different process false should never be
 * returned.  If a oneway call is made to code in the same process (usually to
 * a C++ or Rust implementation), then there are no oneway semantics, and
 * false can still be returned.
 */
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)  
    throws RemoteException;
frameworks/base/core/java/android/os/BinderProxy.java

/**  
 * Java proxy for a native IBinder object. * Allocated and constructed by the native javaObjectforIBinder function. Never allocated * directly from Java code. * * @hide  
 */  
public final class BinderProxy implements IBinder {
    /**  
     * Perform a binder transaction on a proxy. * * @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.  
     * * @return  
     * @throws RemoteException  
     */  
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {  
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");  

        boolean warnOnBlocking = mWarnOnBlocking; // Cache it to reduce volatile access.  

        if (warnOnBlocking && ((flags & FLAG_ONEWAY) == 0)  
                && Binder.sWarnOnBlockingOnCurrentThread.get()) {  

            // For now, avoid spamming the log by disabling after we've logged  
            // about this interface at least once        mWarnOnBlocking = false;  
            warnOnBlocking = false;  

            if (Build.IS_USERDEBUG || Build.IS_ENG) {  
                // Log this as a WTF on userdebug and eng builds.  
                Log.wtf(Binder.TAG,  
                        "Outgoing transactions from this process must be FLAG_ONEWAY",  
                        new Throwable());  
            } else {  
                Log.w(Binder.TAG,  
                        "Outgoing transactions from this process must be FLAG_ONEWAY",  
                        new Throwable());  
            }  
        }  

        final boolean tracingEnabled = Binder.isStackTrackingEnabled();  
        if (tracingEnabled) {  
            final Throwable tr = new Throwable();  
            Binder.getTransactionTracker().addTrace(tr);  
            StackTraceElement stackTraceElement = tr.getStackTrace()[1];  
            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,  
                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());  
        }  

        // Make sure the listener won't change while processing a transaction.  
        final Binder.ProxyTransactListener transactListener = sTransactListener;  
        Object session = null;  

        if (transactListener != null) {  
            final int origWorkSourceUid = Binder.getCallingWorkSourceUid();  
            session = transactListener.onTransactStarted(this, code, flags);  

            // Allow the listener to update the work source uid. We need to update the request  
            // header if the uid is updated.        final int updatedWorkSourceUid = Binder.getCallingWorkSourceUid();  
            if (origWorkSourceUid != updatedWorkSourceUid) {  
                data.replaceCallingWorkSourceUid(updatedWorkSourceUid);  
            }  
        }  

        final AppOpsManager.PausedNotedAppOpsCollection prevCollection =  
                AppOpsManager.pauseNotedAppOpsCollection();  

        if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {  
            flags |= FLAG_COLLECT_NOTED_APP_OPS;  
        }  

        try {  
            final boolean result = transactNative(code, data, reply, flags);  

            if (reply != null && !warnOnBlocking) {  
                reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);  
            }  

            return result;  
        } finally {  
            AppOpsManager.resumeNotedAppOpsCollection(prevCollection);  

            if (transactListener != null) {  
                transactListener.onTransactEnded(session);  
            }  

            if (tracingEnabled) {  
                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);  
            }  
        }  
    }

    /**  
     * Native implementation of transact() for proxies 
     */
    public native boolean transactNative(int code, Parcel data, Parcel reply,  
        int flags) throws RemoteException;
}
frameworks/base/core/jni/android_util_Binder.cpp

static const JNINativeMethod gBinderProxyMethods[] = {  
     /* name, signature, funcPtr */
    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
};

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,  
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException  
{  
    if (dataObj == NULL) {  
        jniThrowNullPointerException(env, NULL);  
        return JNI_FALSE;  
    }  

    Parcel* data = parcelForJavaObject(env, dataObj);  
    if (data == NULL) {  
        return JNI_FALSE;  
    }  
    Parcel* reply = parcelForJavaObject(env, replyObj);  
    if (reply == NULL && replyObj != NULL) {  
        return JNI_FALSE;  
    }  

    IBinder* target = getBPNativeData(env, obj)->mObject.get();  
    if (target == NULL) {  
        jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");  
        return JNI_FALSE;  
    }  

    ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",  
            target, obj, code);  


    bool time_binder_calls;  
    int64_t start_millis;  
    if (kEnableBinderSample) {  
        // Only log the binder call duration for things on the Java-level main thread.  
        // But if we don't        time_binder_calls = should_time_binder_calls();  

        if (time_binder_calls) {  
            start_millis = uptimeMillis();  
        }  
    }  

    //printf("Transact from Java code to %p sending: ", target); data->print();  
    status_t err = target->transact(code, *data, reply, flags);  
    //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();  

    if (kEnableBinderSample) {  
        if (time_binder_calls) {  
            conditionally_log_binder_call(start_millis, target, code);  
        }  
    }  

    if (err == NO_ERROR) {  
        return JNI_TRUE;  
    } else if (err == UNKNOWN_TRANSACTION) {  
        return JNI_FALSE;  
    }  

    signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());  
    return JNI_FALSE;  
}

从这可以看出它的实现在native层,里面进行了一系列的函数调用。

调用链实在太长这里就不给出了;要知道的是它最终调用到了 talkWithDriver 函数;看这个函数的名字就知道,通信过程要交给驱动完成了;最终通过 ioctl 系统调用,Client进程陷入内核态,Client进程挂起等待返回;驱动完成一系列的操作之后唤醒Server进程,调用了Server进程本地对象的 onTransact 函数。我们在看Binder本地对象的 onTransact 方法(这里就是 Stub 类里面的此方法)。

onTransact

@Override  
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {  
    java.lang.String descriptor = DESCRIPTOR;  
    switch (code) {  
        case INTERFACE_TRANSACTION: {  
            reply.writeString(descriptor);  
            return true;  
        }  
        ...
        case TRANSACTION_getThreadPriority: {  
            data.enforceInterface(descriptor);  
            int _arg0;  
            _arg0 = data.readInt();  
            int _result = this.getThreadPriority(_arg0);  
            reply.writeNoException();  
            reply.writeInt(_result);  
            return true;  
        }
        ...
        default: {  
            return super.onTransact(code, data, reply, flags);  
        }  
    }  
}

在Server进程里面, onTransact 根据调用号(每个AIDL函数都有一个编号,在跨进程的时候,不会传递函数,而是传递编号指明调用哪个函数)调用相关函数;在这个例子里面,调用了Binder本地对象的 getThreadPriority 方法;这个方法将结果返回给驱动,驱动唤醒挂起的Client进程并将结果返回。于是一次跨进程调用就完成了。

小结

有一个Stub的抽象类,Stub中又有一个Proxy的抽象类。

Stub.asInterface(IBinder) 会根据是同一进行通信,返回Stub()实体;不同进程通信,返回Stub.Proxy()代理对象。

Stub()的Binder实体中有个onTransact()函数,在前面的一些Binder Native、Framework的流程,我们知道,服务最终处理的入口就是onTransact(),这里会解析Client传来的 TRANSACTION code ,解析Parcel数据,调用对应的服务接口处理。

Proxy()中存在一个asBinder(),返回的对象为mRemote,就如我们前面Framework了解的,对应的其实是BinderProxy对象。

Proxy()组装了Client中的AIDL接口的核心实现,组装Parcel数据,调用BinderProxy()的transact()发送TRANSACTION code。

AIDL通信流程

Client 端和Server端使用同一个AIDL,连包名都需要保持一致。

Server端继承自Service,重载一个onBind(),返回服务实体Stub(),Stub提供了一个asInterface(Binder)的方法,如果是在同一个进程下那么asInterface()将返回Stub对象本身,否则返回Stub.Proxy对象。

IPerformanceService.Stub mStub = new IPerformanceService.Stub(){...};

@Override
public IBinder onBind(Intent intent) {        
    return mStub;//通过ServiceConnection在activity中拿到 PerformanceService
}

Client 绑定服务时通过拿到服务Stub.asInterface返回的服务的代理Stub.Proxy()。

mService = IPerformanceService.Stub.asInterface(service);

Client和Server交互的简单示意流程:

服务本地拿到了AIDL生成的服务实体Stub(), Client绑定服务后,拿到了服务的代理Stub.proxy()。这和我们在前面Framewok层的比较类似了,Client拿到BinderProxy对象,Server拿到Binder实体对象。

AIDL在这里用到了一个Proxy-Stub  (代理-存根)的设计模式,下面我们就这种设计模式来展开说明一下。

Binder通信的数据流转如下图所示:

proxy-stub 设计模式

Proxy将特殊性接口转换成通用性接口,Stub将通用性接口转换成特殊性接口,二者之间的数据转换通过Parcel(打包)进行的,Proxy常作为数据发送代理,通过Parcel将数据打包发送,Stub常作为数据接收桩,解包并解析Parcel Data package。

举个例子

要做的事:我现在要开空调 Client(客户端):我 Proxy(代理):遥控器 Stub(实体):空调, Parcel(数据):遥控器传给空调的蓝牙、红外参数。

我按下了遥控器的提升温度按键,遥控器之前跟空调做了绑定,可以拿到空调的对象实体Stub,把按键的操作组装成一个Parcel数据,发给空调(Server),空调(Server)拿到请求后,执行相应的处理-提升温度,结果返回给遥控器。这样就完成了Proxy-Stub的数据交互流程。

总结

  • AIDL要是实现的最终目标是跨进程访问,简单的说就是得到另一个进程的对象,并调用其方法。
  • AIDL与接口类似,本质属性都是一个Interface(AIDL文件是IInterface,而Interface是继承自Interface的),而且都只定义了抽象方法,没有具体的实现,需要子类去实现。
  • 与接口不同的是:由AIDL生成的stub类本质上是一个Binder!这个类所生成的对象有两种方式可以传递给另外一个进程:
    • 一种是通过bindService的方式,绑定一个服务,而在绑定后,服务将会返回给客户端一个Binder的对象,此时可以把继承自stub的Binder传递给客户端。
    • 另外一种就是把继承自stub的类提升为系统服务,此时,我们通过ServiceManager去得到当前的系统服务,ServiceManager就会把目标Service的Binder对象传递给客户端。
  • 经过上面两种方法得到的Binder对象,就像得到了本地的某个对象一样,可以调用其远程的方法。

扩展知识

aidl 返回值

// IPerformanceService.aidl  
package com.rtfsc.aidldemo;  

interface IPerformanceService {
    void setThreadPriority(int tid, int priority);  
    int getThreadPriority(int pid);
}

setThreadPriority没有返回值,getThreadPriority有返回值;我们来对比 Proxy 对于 setThreadPriority、getThreadPriority 方法的实现如下:

@Override  
public void setThreadPriority(int tid, int priority) 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.writeInt(tid);  
        _data.writeInt(priority);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_setThreadPriority, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            getDefaultImpl().setThreadPriority(tid, priority);  
            return;  
        }  
        _reply.readException();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
}
@Override  
public int getThreadPriority(int pid) 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(pid);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_getThreadPriority, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            return getDefaultImpl().getThreadPriority(pid);  
        }  
        _reply.readException();  
        _result = _reply.readInt();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
    return _result;  
}

可以看到,调用 transact 时都传 reply 进去了。

笔者曾经跟别人争论过这个问题,有些人是认为没有返回值的函数是不需要传 reply 进去的。

oneway

// IPerformanceService.aidl  
package com.rtfsc.aidldemo;  

interface IPerformanceService {
    oneway void setProcessPriority(int pid, int priority);
    void setThreadPriority(int tid, int priority);
}

setProcessPriority 是 oneway,setThreadPriority 不是 oneway。

@Override  
public void setProcessPriority(int pid, int priority) throws android.os.RemoteException {  
    android.os.Parcel _data = android.os.Parcel.obtain();  
    try {  
        _data.writeInterfaceToken(DESCRIPTOR);  
        _data.writeInt(pid);  
        _data.writeInt(priority);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_setProcessPriority, _data, null, android.os.IBinder.FLAG_ONEWAY);  
        if (!_status && getDefaultImpl() != null) {  
            getDefaultImpl().setProcessPriority(pid, priority);  
            return;  
        }  
    } finally {  
        _data.recycle();  
    }  
}
@Override  
public void setThreadPriority(int tid, int priority) 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.writeInt(tid);  
        _data.writeInt(priority);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_setThreadPriority, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            getDefaultImpl().setThreadPriority(tid, priority);  
            return;  
        }  
        _reply.readException();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
}

这里刻意找了个也没有返回值的非oneway接口来做对比,在非oneway接口了,调用 transact 时传 reply 进去了。

而oneway接口是不需要传reply 进去的。并且oneway接口设置的flags是 android.os.IBinder.FLAG_ONEWAY

指定定向tag

两个进程中要传递的对象必须实现**Parcelable**接口,AIDL中序列化的对象传递还必须指定定向tag,tag表示数据的流通方向。 如下 copyArrayIn 指定的入参tag为 in 、如下 copyArrayOut 指定的入参tag为 out 、如下 copyArrayInOut 指定的入参tag为 inout 。

// IPerformanceService.aidl  
package com.rtfsc.aidldemo;  

interface IPerformanceService {
    void copyArrayIn(in String[] source);  
    void copyArrayOut(out String[] source);  
    void copyArrayInOut(inout String[] source);  
}

Proxy 对于 这三个方法的实现如下:

@Override  
public void copyArrayIn(java.lang.String[] source) 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.writeStringArray(source);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_copyArrayIn, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            getDefaultImpl().copyArrayIn(source);  
            return;  
        }  
        _reply.readException();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
}
@Override  
public void copyArrayOut(java.lang.String[] source) throws android.os.RemoteException {  
    android.os.Parcel _data = android.os.Parcel.obtain();  
    android.os.Parcel _reply = android.os.Parcel.obtain();  
    try {  
        _data.writeInterfaceToken(DESCRIPTOR);  
        if ((source == null)) {  
            _data.writeInt(-1);  
        } else {  
            _data.writeInt(source.length);  
        }  
        boolean _status = mRemote.transact(Stub.TRANSACTION_copyArrayOut, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            getDefaultImpl().copyArrayOut(source);  
            return;  
        }  
        _reply.readException();  
        _reply.readStringArray(source);  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
}


Stub类的onTransact方法里:
case TRANSACTION_copyArrayOut: {  
    data.enforceInterface(descriptor);  
    java.lang.String[] _arg0;  
    int _arg0_length = data.readInt();  
    if ((_arg0_length < 0)) {  
        _arg0 = null;  
    } else {  
        _arg0 = new java.lang.String[_arg0_length];  
    }  
    this.copyArrayOut(_arg0);  
    reply.writeNoException();  
    reply.writeStringArray(_arg0);  
    return true;  
}

在 copyArrayIn 函数中,创建Parcel对象_data,将 in 型参数source写入_data,完成序列化。

在 copyArrayOut 函数中,创建Parcel对象_data,写入source.length,调用transact()方法,等待Service端onTransact()响应。 onTransact() 得到String[]的长度;创建String[]变量,并从data中反序列化,得到client端传来的String[]变量;调用本地的 copyArrayOut 方法。 将输出的String[]型_arg0写入Parcel型reply变量,完成序列化;此时onTransact执行完毕。 Proxy将onTransact返回的_reply反序列化,写入source中,source即获得了返回值。整个流程结束。

所以指定 in:client端将参数设定好,传给server,server端拷贝、使用。 所以指定 out:client将接口、地址等“空壳”传给server,server将运算结果填充进去,然后client从中取出运算结果。

@Override  
public void copyArrayInOut(java.lang.String[] source) 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.writeStringArray(source);  
        boolean _status = mRemote.transact(Stub.TRANSACTION_copyArrayInOut, _data, _reply, 0);  
        if (!_status && getDefaultImpl() != null) {  
            getDefaultImpl().copyArrayInOut(source);  
            return;  
        }  
        _reply.readException();  
        _reply.readStringArray(source);  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
}

对比 copyArrayInOut 和 copyArrayIn、copyArrayOut的区别可以知道;如果指定tag为 inout,那么client端既从此处传入数据,又从此处取出server填充的结果。

总结: 需要特别注意的是,对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout。 in:client端将参数设定好,传给server,server端拷贝、使用。 out:client将接口、地址等“空壳”传给server,server将运算结果填充进去,然后client从中取出运算结果。 inout:client端既从此处传入数据,又从此处取出server填充的结果。

将完成in功能的设成out、或out功能的设成in,都会报错。若将连个均设成inout,则通过,但增加了序列化的步骤,白白浪费运算资源。

评论