簡單分析Binder工作機制

Binder 是客戶端和服務(wù)端進行通訊的媒介,當bindService的時候,Service會返回一個包含Srevice業(yè)務(wù)調(diào)用的Binder對象,通過Binder對象,客戶端就可以獲取Service提供的服務(wù)或者數(shù)據(jù),這里Service包括普通的Service和基于AIDl服務(wù)。

Android開發(fā)中,Binder主要在Service中,包括AIDl和Messenger
,其中普通的Service不涉及進程間通訊,而Messenger的底層其實是AIDL,所以分析Binder選中使用AIDL分析Binder的工作機制,

一、創(chuàng)建三個文件Book.java、Book.aidl、IBookManager.aidl,為什么需要定義Book.aidl?因為aidl中只支持:

  • 基本數(shù)據(jù)類型(int、long、char、boolean、double);
  • String和CharSequence;
  • List ,只支持ArrayList,并且元素得是AIDL支持的;
  • Map,只支持HashMap,并且元素得是AIDL支持的,包括key和value;
  • Parcelable,所有實現(xiàn)Parcelable接口的對象;
  • AIDl,所有的AIDL接口本身也可以在AIDL文件中使用,比如自定義類型;

代碼如下:

//Book.java
package com.wfy.article.entity;
import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
private String id;
private String name;

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

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

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(id);
    dest.writeString(name);
}

@Override
public int describeContents() {
    return 0;
}

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];
    }
};
}

需要主要的是Book.aidl的包必須和Book.java相同,Book.aidl是Book.java的定義;

// Book.aidl
package com.wfy.article.entity;

// Declare any non-default types here with import statements
parcelable Book;

定義相關(guān)操作:

// IBookManager.aidl
  package com.wfy.article;

  import com.wfy.article.entity.Book;

  interface IBookManager {
        void addBook(in Book book);
        List<Book> getBooks();
  }

開始分析Binder,分析由系統(tǒng)生成 IBookManager.aidl的java文件:

/*
  * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\AndroidStudioprojects\\qihe\\AndroidArticleFind\\app\\src\\main\\aidl\\com\\wfy\\article\\IBookManager.aidl
 */
package com.wfy.article;

public interface IBookManager extends android.os.IInterface {
/**
 * Local-side IPC implementation stub class.
 */
public static abstract class Stub extends android.os.Binder implements com.wfy.article.IBookManager {


    /**
     * Binder 的唯一標識
     */
    private static final java.lang.String DESCRIPTOR = "com.wfy.article.IBookManager";

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

    /**
     * 用于將Service的Binder對象轉(zhuǎn)換成客戶端所需的AIDL接口類型的對象,轉(zhuǎn)換過程是區(qū)分進程的,
     * <p>
     * 如果客戶端的進程和Service進程位于同一個進程,那么此方法返回Stub對象本身,否則返回的是系統(tǒng)封裝的Stub.proxy對象來完成transact過程
     * <p>
     * <p>
     * Cast an IBinder object into an com.wfy.article.IBookManager interface,
     * generating a proxy if needed.
     */
    public static com.wfy.article.IBookManager asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }

      /**
         * 嘗試檢索實現(xiàn)Binder的本地接口的對象,如果返回空值,則需要將代理類實例化調(diào)用transact方法
         *
         * 具體可以輸出Log,同進程返回Stub,否則返回Stub.Proxy(用于transact)
         */
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.wfy.article.IBookManager))) {
            return ((com.wfy.article.IBookManager) iin);
        }
        return new com.wfy.article.IBookManager.Stub.Proxy(obj);
    }


    /**
     * 返回當前Binder對象
     *
     * @return
     */
    @Override
    public android.os.IBinder asBinder() {
        return this;
    }


    /**
     * 此方法運行在Binder線程池中
     * <p>
     * Service通過code可以確定客戶端所請求的目標方法是什么,接著從data中取出目標方法所需的參數(shù),然后執(zhí)行目標方法。
     * <p>
     * 當目標方法執(zhí)行完畢后,就向reply中寫入返回值
     * <p>
     * <p>
     * <p>
     * 如果此方法返回 false,那么客戶端請求會失敗
     *
     * @param code
     * @param data
     * @param reply
     * @param flags
     * @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 {
        java.lang.String descriptor = DESCRIPTOR;
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_addBook: {
                data.enforceInterface(descriptor);
                com.wfy.article.entity.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.wfy.article.entity.Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_getBooks: {
                data.enforceInterface(descriptor);
                java.util.List<com.wfy.article.entity.Book> _result = this.getBooks();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }

    private static class Proxy implements com.wfy.article.IBookManager {
        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;
        }

        /**
         * 和getBooks()一樣
         *
         * @param book
         * @throws android.os.RemoteException
         */
        @Override
        public void addBook(com.wfy.article.entity.Book book) throws android.os.RemoteException {

            // 創(chuàng)建輸入對象(該方法的參數(shù)信息等)
            android.os.Parcel _data = android.os.Parcel.obtain();
            // 創(chuàng)建輸出對象(RPC過程返回數(shù)據(jù))
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((book != null)) {
                    _data.writeInt(1);

                    // 寫入?yún)?shù)數(shù)據(jù)
                    book.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
                }

                //發(fā)起RPC過程 ,這里是會阻塞的,等待transact結(jié)束
                mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);

                _reply.readException();

                // 因為沒有返回值,所以不需要從_reply中取出RPC過程數(shù)據(jù)
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }

        /**
         * 此方法運行在客戶端
         *
         * @return
         * @throws android.os.RemoteException
         */
        @Override
        public java.util.List<com.wfy.article.entity.Book> getBooks() throws android.os.RemoteException {

            // 創(chuàng)建輸入對象(該方法的參數(shù)信息等)
            android.os.Parcel _data = android.os.Parcel.obtain();
            // 創(chuàng)建輸出對象(RPC過程返回數(shù)據(jù))
            android.os.Parcel _reply = android.os.Parcel.obtain();

            //返回值
            java.util.List<com.wfy.article.entity.Book> _result;

            try {
                //參數(shù)信息寫入_data中
                _data.writeInterfaceToken(DESCRIPTOR);

                // 發(fā)起RPC請求,同時當前線程掛起,然后Service端的onTransact方法會被調(diào)用,直到RPC過程返回后,線程繼續(xù)執(zhí)行,
                mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);

                //  接著從_reply中取出RPC過程返回的結(jié)果
                _reply.readException();

                //  最后返回_reply中的數(shù)據(jù)
                _result = _reply.createTypedArrayList(com.wfy.article.entity.Book.CREATOR);
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }


    /**
     * 各方法的id
     */
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}

public void addBook(com.wfy.article.entity.Book book) throws android.os.RemoteException;

public java.util.List<com.wfy.article.entity.Book> getBooks() throws android.os.RemoteException;
}

來一波圖


Binder工作機制.png

1、當客戶端發(fā)起請求,當前線程掛起直至Service進程返回數(shù)據(jù),如果遠程方法耗時,不能再UI線程中執(zhí)行;
2、由于Binder方法運行在Binder線程池中,所以Binder方法不管是否耗時都應(yīng)該采用同步的方式去實現(xiàn);
3、IBookManager 中聲明了兩個整形id分別用于標識getBooks和addBook這兩個方法,內(nèi)部還有一個Stub,Stub是一個Binder的實現(xiàn)類,當客戶端和Service同一個進程時,此方法不會走跨進程transact過程,否則走進程transact過程,由Stub.proxy來完成該過程,可以詳細可以看看Stub中asInterface方法,同一個進程直接返回Stub對象,否則返回Stub.proxy。

Binder機制引用此文章Android Bander設(shè)計與實現(xiàn) - 設(shè)計篇

Binder框架定義了四個角色:Server,Client,ServiceManager(以后簡稱SMgr)以及Binder驅(qū)動。其中Server,Client,SMgr運行于用戶空間,驅(qū)動運行于內(nèi)核空間。這四個角色的關(guān)系和互聯(lián)網(wǎng)類似:Server是服務(wù)器,Client是客戶終端,SMgr是域名服務(wù)器(DNS),驅(qū)動是路由器。

Binder 驅(qū)動

和路由器一樣,Binder驅(qū)動雖然默默無聞,卻是通信的核心。盡管名叫‘驅(qū)動’,實際上和硬件設(shè)備沒有任何關(guān)系,只是實現(xiàn)方式和設(shè)備驅(qū)動程序是一樣的。它工作于內(nèi)核態(tài),驅(qū)動負責(zé)進程之間Binder通信的建立,Binder在進程之間的傳遞,Binder引用計數(shù)管理,數(shù)據(jù)包在進程之間的傳遞和交互等一系列底層支持。

ServiceManager 與實名Binder

和DNS類似,SMgr的作用是將字符(域名)形式的Binder名字轉(zhuǎn)化成Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder實體(IP)的引用。注冊了名字的Binder叫實名Binder,就象每個網(wǎng)站除了有IP地址外還有自己的網(wǎng)址。Server創(chuàng)建了Binder實體對象,為其取一個字符形式,可讀易記的名字,將這個Binder連同名字以數(shù)據(jù)包的形式通過Binder驅(qū)動發(fā)送給SMgr,通知SMgr注冊一個名叫張三的Binder,它位于某個Server中。驅(qū)動為這個穿過進程邊界的Binder創(chuàng)建位于內(nèi)核中的實體節(jié)點以及SMgr對實體的引用,將名字及新建的引用打包傳遞給SMgr。SMgr收數(shù)據(jù)包后,從中取出名字和引用填入一張查找表中。

簡單講就是Serverce進程創(chuàng)建服務(wù),按照以往那種通過數(shù)據(jù)包交互就非常復(fù)雜,所以引入ServiceManager對Service提供注冊Binder實體引用,在Client中直接使用這個實體引用即可使用Service提供的服務(wù)了。

Client 獲得實名Binder的引用

Server向SMgr注冊了Binder實體(IP)及其名字(域名)后,Client就可以通過名字(域名)獲得該Binder的引用了。Client也利用保留的0號引用向SMgr請求訪問某個Binder。SMgr收到這個連接請求,從請求數(shù)據(jù)包里獲得Binder的名字,在查找表里找到該名字對應(yīng)的條目,從條目中取出Binder的引用,將該引用作為回復(fù)發(fā)送給發(fā)起請求的Client。從面向?qū)ο蟮慕嵌龋@個Binder對象現(xiàn)在有了兩個引用:一個位于SMgr中,一個位于發(fā)起請求的Client中。如果接下來有更多的Client請求該Binder,系統(tǒng)中就會有更多的引用指向該Binder,就象java里一個對象存在多個引用一樣。而且類似的這些指向Binder的引用是強類型,從而確保只要有引用Binder實體就不會被釋放掉。
看一張圖壓壓驚:


binder原理.png

具體內(nèi)存映射:
Linux內(nèi)核實際上沒有從一個用戶空間到另一個用戶空間直接拷貝的函數(shù),需要先用copy_from_user()拷貝到內(nèi)核空間,再用copy_to_user()拷貝到另一個用戶空間。為了實現(xiàn)用戶空間到用戶空間的拷貝,劃重點mmap()分配的內(nèi)存除了映射進了接收方進程里,還映射進了 內(nèi)核空。所以調(diào)用copy_from_user()將數(shù)據(jù)拷貝進內(nèi)核空間也相當于拷貝進了接收方的用戶空間,這就是Binder只需一次拷貝的‘秘密’。

binder內(nèi)存映射.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容