Android Binder機制入門

Binder機制是什么?

僅從應用層上來講:

  • Binder是一個類,實現(xiàn)了IBinder接口
  • Binder是android中的一種跨進程通信方式,Binder基于C/S模型,是客戶端和服務端通信的一種媒介,當執(zhí)行bindService的時候,服務端會返回一個IBinder對象,客戶端可以根據(jù)這個IBinder對象獲取相應的服務端的服務。

為什么要使用Binder

android中的IPC機制雖然有很多中,比如文件共享,Socket,Messager,ContentProvider,四大組件間通過Bundle傳遞數(shù)據(jù)等(Messager,ContentProvider都是通過封裝Binder實現(xiàn)的),但是只有Binder機制能很好的實現(xiàn)RPC(遠程服務調(diào)用),并且在高并發(fā)的情況下,Binder能更好的處理好線程同步問題。

Binder的使用Demo

實現(xiàn)Binder有兩中方式:

  • 定義好對應的AIDL接口,使用android studio等IDE自動生成Binder的實現(xiàn)類。
  • 自己手寫B(tài)inder實現(xiàn)類。

通過AIDL生成Binder

首先,aidl支持的數(shù)據(jù)類型有:

  • 所有的基本類型
  • String
  • CharSequence
  • List
  • Map
  • 實現(xiàn)了Parcelable接口的自定義類型
  1. 所以如果我們需要傳輸自定義類型,則需要實現(xiàn)Parcelable接口:
public class Book implements Parcelable {
  1. 編寫aidl接口:
import com.jason.binderdemo.Book;
interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    List<Book> getBookList();

    void addBook(in Book book);
}

注意,如果使用自定義的類型,需要手動import包名,并且需要在生成一個同名的aidl文件:

// Book.aidl.aidl
package com.jason.binderdemo;

// Declare any non-default types here with import statements

parcelable Book;
  1. 編譯后,IDE會為我們自動生成一個與aidl接口同名的interface:

可以在build/generated/source/aidl文件夾下找到

public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jason.binderdemo.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.jason.binderdemo.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.jason.binderdemo.IBookManager interface,
 * generating a proxy if needed.
 */
public static com.jason.binderdemo.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jason.binderdemo.IBookManager))) {
return ((com.jason.binderdemo.IBookManager)iin);
}
return new com.jason.binderdemo.IBookManager.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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jason.binderdemo.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.jason.binderdemo.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.jason.binderdemo.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jason.binderdemo.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;
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
@Override public java.util.List<com.jason.binderdemo.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.jason.binderdemo.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.jason.binderdemo.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.jason.binderdemo.Book book) 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 ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
public java.util.List<com.jason.binderdemo.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.jason.binderdemo.Book book) throws android.os.RemoteException;
}

這個類主要由一個接口和他的實現(xiàn)類Stub組成,而Stub又繼承自Binder類,也就是為我們自動生成了一個包含aidl中聲明方法的Binder類。
其實第二種生成的Binder的方法也就是我們自己去編寫這個類

  1. 在服務端的onBind方法中返回binder對象:
    IBookManager.Stub mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mList.add(book);
        }
    };
    ...
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
  1. 在客戶端調(diào)用bindService通過ServiceConnection拿到服務端的接口服務:
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
         //   mRemote = IBookManager.Stub.asInterface(service);
            mRemote = IBBookManagerImpl.asInterface(service);
            isBind = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemote = null;
            isBind = false;
        }
    };
    ...
    @Override
    protected void onStart() {
        super.onStart();
        if (!isBind) {
            Intent intent = new Intent(this, RemoteService.class);
            bindService(intent, connection, BIND_AUTO_CREATE);
        }
    }
  1. 設置Binder死亡的回調(diào):
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mRemote != null) {
                mRemote.asBinder().unlinkToDeath(mDeathRecipient, 0);
                mRemote = null;
                //重新綁定服務
            }
        }
    };
    ...
         public void onServiceConnected(ComponentName name, IBinder service) {
         //   mRemote = IBookManager.Stub.asInterface(service);
            mRemote = IBBookManagerImpl.asInterface(service);
            try {
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            isBind = true;
        }
    ...
  1. 通過調(diào)用服務端暴露的接口調(diào)用對應的操作:
    public void addBook(View v) {
        Book book = new Book(3, "got3");
        try {
            mRemote.addBook(book);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void getBook(View v){
        try {
            Book book = mRemote.getBookList().get(0);
            mTvInfo.setText(book.bookId + "\n" + book.bookName);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

手動編寫B(tài)inder的實現(xiàn)類

  1. 參照系統(tǒng)生成的IBookManger接口,手寫一個類似的接口:
public interface IBBookManager extends IInterface{

    static final String DESCRIPTOR = "com.jason.binderdemo.IBookManager"; //Binder的標記

    static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0; //標記調(diào)用哪個方法
    static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;

    List<Book> getBookList() throws RemoteException; //aidl文件中聲明的接口
    void addBook(Book book) throws RemoteException;

}

這個接口包含了服務端提供的服務以及對應方法的標記字段和用于區(qū)別其他Binder的DESCRIPTOR字段

  1. 編寫一個繼承自Binder并且實現(xiàn)IBBookManager接口的抽象類:
public abstract class IBBookManagerImpl extends Binder implements IBBookManager {

    public IBBookManagerImpl() {
        //系統(tǒng)的構造方法
        this.attachInterface(this, DESCRIPTOR);
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;
            case TRANSACTION_getBookList:
                data.enforceInterface(DESCRIPTOR);
                List<Book> result = this.getBookList(); //接收到客戶端的請求后調(diào)用本地的方法
                reply.writeNoException();
                reply.writeTypedList(result); //在相應中寫入數(shù)據(jù)
                return true;
            case TRANSACTION_addBook:
                data.enforceInterface(DESCRIPTOR);
                Book arg0;
                if (0!=data.readInt()) {
                    arg0 = Book.CREATOR.createFromParcel(data); //將參數(shù)反序列化為BOOK對象
                }else {
                    arg0 = null;
                }
                this.addBook(arg0); //調(diào)用本地方法
                reply.writeNoException();
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    public static IBBookManager asInterface(IBinder obj) { //為客戶端暴露服務端提供的服務
        if (obj == null) {
            return null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin != null && iin instanceof IBBookManager) {
            //跑在統(tǒng)一進程中,其實就是返回當前這個對象,即在構造函數(shù)中傳入的this
            //調(diào)用方法時不需要通過transact過程
            return ((IBBookManager)iin);
        }else {
            //不同進程通過代理類來完成跨進程的調(diào)用
            return new Proxy(obj);
        }
    }
  1. 編寫代理類
    private static class Proxy implements IBBookManager {

        private IBinder mRemote;

        public Proxy(IBinder remote) {
            this.mRemote = remote;
        }

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List<Book> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR); //寫入Binder信息用于驗證
                mRemote.transact(TRANSACTION_getBookList, data, reply, 0); //跨進程發(fā)生在這里
                reply.readException();
                result = reply.createTypedArrayList(Book.CREATOR); //獲取服務端回應的數(shù)據(jù)
            }finally {
                reply.recycle();
                data.recycle();
            }
            return result;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);//寫入Binder信息用于驗證
                if (book != null) {
                    data.writeInt(1); 
                    book.writeToParcel(data, 0); //寫入?yún)?shù)信息
                }else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addBook, data, reply, 0);
                reply.readException();
            }finally {
                reply.recycle();
                data.recycle();
            }
        }

        @Override
        public IBinder asBinder() {
            return mRemote;
        }
    }

接下里就可以用編寫的這幾個類進行跨進程通信了。aidl只是系統(tǒng)提供了一個快速實現(xiàn)Binder的方法。

Binder機制的調(diào)用解析

Binder工作流程

以上為服務端和客戶端跑在不同進程的調(diào)用過程,如果服務端和客戶端是在同一進程就可以直接調(diào)用服務端的方法獲取結果。

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

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

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