aidl涉及的Binder框架流程簡單分析

前言:IPC機制必然會碰到AIDL,網上相關的博客很多,但總不如自己走一邊來的印象深刻。

預備的所有文件

關於如何使用AIDL,網上很多也比較簡單,不贅述了。

Book.aidl

// Book.aidl
package com.lct.zyw.serviceaidl;

parcelable Book;

Book.java

package com.lct.zyw.serviceaidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by zhangyawen on 2016/9/14.
 */
public class Book implements Parcelable{
    private int id;
    private String name;

    public Book() {
    }

    public Book(int id, String name) {
        this.id = id;
        this.name = name;
    }

    protected Book(Parcel in) {
        id = in.readInt();
        name = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * Describe the kinds of special objects contained in this Parcelable's
     * marshalled representation.
     *
     * @return a bitmask indicating the set of special object types marshalled
     * by the Parcelable.
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Flatten this object in to a Parcel.
     *
     * @param dest  The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    /**
     * 用於AIDL接口方法中Book類型的參數的tag爲out,inout。
     * 如果該方法改成空實現,則服務端對參數的任何修改不會改變客戶端傳入的參數,其實最關鍵的是搞懂IBookManagerInterface.java的流程,就能知其所以然了。
     * @param source
     */
    public void readFromParcel(Parcel source) {
        id = source.readInt();
        name = source.readString();
    }

    @Override
    public String toString() {
        return "Book { id: "+this.id+", name: "+this.name+"}";
    }
}

IBookManagerinterface.aidl

// IBookManagerInterface.aidl
// android studio 不會自動生成對應aidl文件的java文件,具體做法是在aidl文件下Ctrl+ F9(Make Project)
// build成功之後在對應項目文件路徑下的\build\generated\source\aidl\debug中就會生成aidl文件的對應的java文件
package com.lct.zyw.serviceaidl;
//需要手動導入(即使在同一個包下)
import com.lct.zyw.serviceaidl.Book;

interface IBookManagerInterface {
    void doSomething(int x,int y);
    List<Book> getBooks();
    //關於定向Tag,在對應的java文件中需要深入分析
    Book addBookIn(in Book book);
    Book addBookOut(out Book book);
    Book addBookInout(inout Book book);

}

分析IBookManagerinteraface.java

網上很多時從服務端代碼往客戶端代碼分析的,我是從客戶端代碼往服務端代碼分析的,其實都差不多,個人習慣而已

IBookManagerInterface.java

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\ServiceAIDL\\serviceprot\\src\\main\\aidl\\com\\lct\\zyw\\serviceaidl\\IBookManagerInterface.aidl
 */

/* *
 * Binder的框架:
 * 1.Android Binder框架分爲服務器接口、Binder驅動、以及客戶端接口;簡單想一下,需要提供一個全局服務,
 *   那麼全局服務那端即是服務器接口,任何程序即客戶端接口,它們之間通過一個Binder驅動訪問。
 * 2.服務器端接口:實際上是Binder類的對象,該對象一旦創建,內部則會啓動一個隱藏線程,會接收Binder驅動發送的消息,
 *   收到消息後,會執行Binder對象中的onTransact()函數,並按照該函數的參數執行不同的服務器端代碼。
 * 3.Binder驅動:該對象也爲Binder類的實例,客戶端通過該對象訪問遠程服務。
 * 4.客戶端接口:獲得Binder驅動,調用其transact()發送消息至服務器
 * */

package com.lct.zyw.serviceaidl;

/**
 * 1.接口的繼承仍然用[extends]關鍵字
 * 2.所有可以在Binder中傳輸的接口都需要繼承IInterface接口
 * 3.雖然該Java文件時根據對應的aidl文件由android studio自動生成的,在理解細節實現之後,就可以不依賴aidl文件,
 *   直接手寫IBookManagerInterface.java,甚至將該java文件中的代碼拆分到服務端和客戶端中去。
 *
 */
public interface IBookManagerInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     * 聲明瞭一個Stub的靜態抽象內部類,繼承了Binder,即使一個Binder的子類,實現IBookManagerInterface接口
     * 說明:1.抽象類實現接口,則接口中的抽象方法可以不在抽象方法中實現,交由繼承該抽象類的實現類(非抽象類)來實現,此時就必須實現。
     *      2.若抽象類中實現了接口中的抽象方法,那麼實現類(繼承自抽象類)就可以不實現,也可以實現(方法的重寫)。
     */
    public static abstract class Stub extends android.os.Binder implements com.lct.zyw.serviceaidl.IBookManagerInterface {
        //Binder類(Stub)的唯一標識,一般以類名錶示
        private static final java.lang.String DESCRIPTOR = "com.lct.zyw.serviceaidl.IBookManagerInterface";

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

        /**
         * Cast an IBinder object into an com.lct.zyw.serviceaidl.IBookManagerInterface interface,
         * generating a proxy if needed.
         * 將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象
         * obj:客戶端綁定服務端成功後,ServiceConnection中的onServiceConnected方法被調用,返回的service(Ibinder接口)
         */
        public static com.lct.zyw.serviceaidl.IBookManagerInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //通過標識,檢驗obj是否屬於本進程(客戶端)中的Ibinder
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.lct.zyw.serviceaidl.IBookManagerInterface))) {
                return ((com.lct.zyw.serviceaidl.IBookManagerInterface) iin);
            }
            //進程間通信最終返回客戶端的是Stub類中的內部代理類Proxy對象,那麼在客戶端的任何服務端方法調用都是Proxy類中對應方法的調用。
            return new com.lct.zyw.serviceaidl.IBookManagerInterface.Stub.Proxy(obj);
        }

        /**
         * IInterface接口中的方法 一個Binder類返回一個Ibinder接口(Binder實現了Ibinder接口),
         * 這其實是一種向上轉型。
         * @return
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 該方法時通過Proxy類中的transact方法發起的
         * @param code 方法ID:和Proxy類中的transact方法中的一一對應
         * @param data 客戶端傳遞過來的參數
         * @param reply 服務器返回回去的值
         * @param flags 標明是否有返回值,0爲有(雙向),1爲沒有(單向)
         * @return
         * @throws android.os.RemoteException
         */
        @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_doSomething: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.doSomething(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getBooks: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.lct.zyw.serviceaidl.Book> _result = this.getBooks();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBookIn: {
                    //與客戶端的writeInterfaceToken對用,標識遠程服務的名稱
                    data.enforceInterface(DESCRIPTOR);
                    //將data轉換成Book類
                    com.lct.zyw.serviceaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //調用服務端 new IBookManagerInterface.Stub()實現類的具體方法
                    com.lct.zyw.serviceaidl.Book _result = this.addBookIn(_arg0);
                    //將返回值_result轉換成 reply,返回給客戶端
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_addBookOut: {
                    data.enforceInterface(DESCRIPTOR);
                    com.lct.zyw.serviceaidl.Book _arg0;
                    /**
                     * 相比tag爲in,服務端僅僅自己new一個Book類的對象,參數_data並沒有對book對象進行賦值操作
                     *              而tag爲in的方法是有賦值操作的
                     */
                    _arg0 = new com.lct.zyw.serviceaidl.Book();
                    com.lct.zyw.serviceaidl.Book _result = this.addBookOut(_arg0);
                    reply.writeNoException();
                    /**
                     * 此處將服務端返回的_result(Book)賦值給reply(Parcel)(不管定向Tag是什麼,操作都是一樣的,不解釋)
                     */
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    /**
                     * Note:
                     * 相比tag爲in,此處代碼是多出來的,這裏是將服務端的入參_arg0賦值給reply,那就說服務端方法的入參就是返回值,
                     *              如果服務端方法中不對入參_arg0做任何操作,那麼返回的將是一個新new出來的Book對象,需注意。
                     */
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_addBookInout: {
                    data.enforceInterface(DESCRIPTOR);
                    com.lct.zyw.serviceaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    com.lct.zyw.serviceaidl.Book _result = this.addBookInout(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        /**
         * 客戶端綁定服務端成功後,執行的直接方法都是內部代理Proxy類中對應的方法
         */
        private static class Proxy implements com.lct.zyw.serviceaidl.IBookManagerInterface {

            private android.os.IBinder mRemote;

            //mRemote被賦值:客戶端的傳進來的service(Ibinder接口類型)
            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 doSomething(int x, int y) 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(x);
                    _data.writeInt(y);
                    mRemote.transact(Stub.TRANSACTION_doSomething, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.util.List<com.lct.zyw.serviceaidl.Book> getBooks() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.lct.zyw.serviceaidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.lct.zyw.serviceaidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
                /**
                 * _data: 輸入型Parcel對象
                 * _reply: 輸出型Parcel對象
                 * _result: 返回值對象(類型根據返回值類型而定)
                 */
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lct.zyw.serviceaidl.Book _result;

                try {
                    //服務器端的enforceInterfac對應
                    _data.writeInterfaceToken(DESCRIPTOR);

                    //將寫入參數到_data中去(此處也證明了關於進程間通信的自定義類型的參數對象(Book),必須是實現Parcelable接口的)
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    /**
                     * transact(int code, Parcel data, Parcel reply, int flags)方法對應的參數及自身說明
                     *
                     * code: 方法ID,唯一性,一一對應,服務端onTransact方法會根據客戶端傳過來的方法ID來確定調用具體的方法
                     * data: 用來存儲客戶端傳遞給服務端的Parcel類型的數據
                     * reply: 用來存儲客戶端傳回給客戶端的Parcel類型的數據
                     * flags: 是否有返回值:爲 0 表示數據可以雙向流通,即 _reply 流可以正常的攜帶數據回來,
                     *                     爲 1 的話那麼數據將只能單向流通,從服務端回來的 _reply 流將不攜帶任何數據
                     *        Note:默認爲0,不解釋。
                     *
                     * 調用該方法來發起RPC(遠程過程調用)請求,同時當前線程掛起;然後服務端的onTransact方法會被調用,
                     * 直到RPC過程返回後,當前線程繼續執行
                     * Note:此處提到了線程掛起,說明RPC請求很可能是耗時操作,那麼在客戶端調用服務端的方法時,最好避免直接在UI
                     *       線程中直接調用,以免引起ANR,另起線程更安全
                     */
                    mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
                    //_reply接受到服務端的數據,轉換成返回值的數據類型(Book)返回給客戶端
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    /**
                     * Recycle 機制(Note: 並不是java虛擬機的垃圾回收機制)
                     * 簡釋:
                     * 當一個對象不再使用時把它儲藏起來,不讓虛擬機回收,需要的時候再從倉庫裏拿出來重新使用,
                     * 這就避免了對象被回收後再重分配的過程。對於在應用的生命週期內(或者在循環中)需要頻繁
                     * 創建的對象來說這個機制特別實用,可以顯著減少對象創建的次數,從而減少 GC 的運行時間。
                     * 運用得當便可改善應用的性能。唯一的不足只是需要手動爲廢棄對象調用 recycle 方法。
                     * 文/TiouLims(簡書作者)
                     * 原文鏈接:http://www.jianshu.com/p/5cba251c7fd9
                     */
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            /**
             * 關於定向Tag(in out inout)的分析(Note:在aidl文件中方法的傳入參數設置定向Tag,最根本的還是android studio會根據Tag
             *                                      在對應java代碼中方法的實現上有區別)
             * 該方法的傳入參數的定向tag在aidl文件中定義爲out。
             * @param book
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
                /**
                 * _data: 輸入型Parcel對象
                 * _reply: 輸出型Parcel對象
                 * _result: 返回值對象(類型根據返回值類型而定)
                 */
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lct.zyw.serviceaidl.Book _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    /**
                     * 相比tag爲in,該方法的入參book並沒有轉化成_data,通過transcat方法傳遞給服務端,那就是說,參數定向tag爲out的方法
                     * 服務端將不接受的_data參數(Ontransact方法中說明)
                     */
                    mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
                    _reply.readException();
                    /**
                     * 此處將服務端返回的_reply轉化成Book對象,並返回給客戶端(不管定向Tag是什麼,操作都是一樣的,不解釋)
                     */
                    if ((0 != _reply.readInt())) {
                        _result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                    /**
                     * 相比tag爲in,客戶端的入參book被重新賦值,關鍵在Book.java中需要額外的實現readFromParcel方法,
                     *              那麼客戶端的入參book到底會有什麼變化,主要看readFromParcel方法的具體實現:
                     *              1.參照Book.java中的構造函數Book(Parcel in)實現,那麼入參將和返回值相同
                     *              2.readFromParcel是空實現,那麼入參沒有任何變化
                     */
                    if ((0 != _reply.readInt())) {
                        book.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            /**
             * 定向Tag爲inout,則方法實現是同時擁有in和out的並集效果,相對的底層的開銷也會比較大,沒有必要就不要這麼做了。
             * @param book
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lct.zyw.serviceaidl.Book _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                    if ((0 != _reply.readInt())) {
                        book.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        //在aidl中定義的方法都會在此得到唯一的ID
        static final int TRANSACTION_doSomething = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addBookIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addBookOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_addBookInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
    }

    public void doSomething(int x, int y) throws android.os.RemoteException;

    public java.util.List<com.lct.zyw.serviceaidl.Book> getBooks() throws android.os.RemoteException;

    public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;

    public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;

    public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
}

擴展鏈接:對Binder的淺顯分析及AIDL的使用

結語:好記性不如爛筆頭,寫下來,方便以後查找。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章