Android中AIDL的工作原理

在上一篇文章中Android中AIDL的使用詳解介紹了AIDL的使用流程,這篇文章我們說說AIDL的工作原理。

IPC

在這之前我們先簡單說一下IPC,IPC是Inter-Process Communication的縮寫,是進程間通信或者跨進程通信的意思,既然說到進程,大家要區分一下進程和線程,進程一般指的是一個執行單元,它擁有獨立的地址空間,也就是一個應用或者一個程序。線程是CPU調度的最小單元,是進程中的一個執行部分或者說是執行體,兩者之間是包含與被包含的關系。因為進程間的資源不能共享的,所以每個系統都有自己的IPC機制,Android是基于Linux內核的移動操作系統,但它并沒有繼承Linux的IPC機制,而是有著自己的一套IPC機制。

Binder

Binder就是Android中最具特色的IPC方式,AIDL其實就是通過Binder實現的,因為在我們定義好aidl文件后,studio就幫我們生成了相關的Binder類。事實上我們在使用AIDL時候繼承的Stub類,就是studio幫我們生成的Binder類,所以我們可以通過查看studio生成的代碼來了解Binder的工作原理。首先我們定義一個AIDL文件

// UserManager.aidl
package cc.abto.demo;

interface UserManager {

    String getName();

    String getOtherName();
}

sycn project工程后,查看生成的UserManager.java文件

package cc.abto.demo;
// Declare any non-default types here with import statements

public interface UserManager extends android.os.IInterface
{

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements cc.abto.demo.UserManager
    {
       //...
    }

    public java.lang.String getName() throws android.os.RemoteException;

    public java.lang.String getOtherName() throws android.os.RemoteException;
}                

生成的代碼還是比較多的,我就不一次性全部貼上來了,先按類結構來看。

sutido幫我們生成了一個繼承android.os.IInterface接口的UserManager接口,所有在Binder中傳輸的接口都必須實現IInterface接口。接口定義了我們在AIDL文件中定義的方法,然后還有個內部靜態類Stub,我們接著看這個Stub。

public static abstract class Stub extends android.os.Binder implements cc.abto.demo.UserManager
    {

        private static final java.lang.String DESCRIPTOR = "cc.abto.demo.UserManager";

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

        /**
         * Cast an IBinder object into an cc.abto.demo.UserManager interface,
         * generating a proxy if needed.
         */
        public static cc.abto.demo.UserManager asInterface(android.os.IBinder obj)
        {

            if ((obj == null))
            {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof cc.abto.demo.UserManager)))
            {
                return ((cc.abto.demo.UserManager) iin);
            }
            return new cc.abto.demo.UserManager.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
        {

            switch (code)
            {
                case INTERFACE_TRANSACTION:
                {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getName:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_getOtherName:
                {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getOtherName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements cc.abto.demo.UserManager
        {
           //...
        }

        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        static final int TRANSACTION_getOtherName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

Stub繼承了android.os.Binder并實現UserManager接口,下圖是Stub的類結構。


我們可以看到Stub中的常量,其中兩個int常量是用來標識我們在接口中定義的方法的,DESCRIPTOR常量是 Binder的唯一標識。
asInterface 方法用于將服務端的Binder對象轉換為客戶端所需要的接口對象,該過程區分進程,如果進程一樣,就返回服務端Stub對象本身,否則呢就返回封裝后的Stub.Proxy對象。
onTransact 方法是運行在服務端的Binder線程中的,當客戶端發起遠程請求后,在底層封裝后會交由此方法來處理。通過code來區分客戶端請求的方法,注意一點的是,如果該方法返回false的換,客戶端的請求就會失敗。一般可以用來做權限控制。
最后我們來看一下Proxy代理類。

private static class Proxy implements cc.abto.demo.UserManager
{

    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 java.lang.String getName() throws android.os.RemoteException
    {

        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try
        {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
        }
        finally
        {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public java.lang.String getOtherName() throws android.os.RemoteException
    {

        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try
        {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getOtherName, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
        }
        finally
        {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

代理類中我們主要看一下getName和getOtherName方法就可以了,這兩個方法都是運行在客戶端,當客戶端發起遠程請求時,_data會寫入參數,當然這邊的例子并沒有(啦啦啦...),然后調用transact方法發起RPC(遠程過程調用)請求,同時掛起當前線程,然后服務端的onTransact方法就會被 調起,直到RPC過程返回后,當前線程繼續執行,并從_reply取出返回值(如果有的話),并返回結果。

最后

分析完sutido生成的Binder之后,我們就大概知道AIDL的工作原理,定義好AIDL文件只是方便sutido幫我生成所需的Binder類,AIDL并不是必須的文件,因為這個Binder類我們也可以手寫出來(當然,你閑的沒事的話),所以這邊最重要的還是Binder的知識點,其他一些IPC方式其實都是通過Binder來實現的,比如說Messager,Bundle,ContentProvider,只是它們的封裝方式不一樣而已。總的來說,從應用層來說,Binder是客戶端和服務端之間通信的媒介。從FrameWork層來說,Binder是ServiceManager連接各種Manager和ManagerService的橋梁。Android系統中充斥著大量的CS模型,而Binder作為獨有的IPC方式,如果我們能更好的理解它,對我們的開發工作就會帶來更多的幫助。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容