Binder是Android的一個類,它實現了IBinder接口。從IPC角度來講,Binder是Android中的一種跨進程通訊方式;從Android應用層來講,Binder是客戶端和服務端進行通訊的媒介,當bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,經過這個Binder對象,客戶端就能夠獲取服務端提供的服務或者數據,這裏的服務包括普通服務和基於AIDL的服務。java
在Android開發中,Binder主要用在Service中,包括AIDL(Android Interface Defination Language)和Messenger,其中普通Service中的Binder不涉及進程間通訊,因此較爲簡單,沒法觸及Binder的核心,而Messenger的底層實際上是AIDL,因此這裏選擇AIDL來分析Binder的工做機制。android
新建三個文件,Book.java、Book.aidl和IBookManager.aidl,代碼以下: Book.javabash
package com.example.runningh.myapplication.aidl; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() { @Override public Book createFromParcel(Parcel source) { return new Book(source); } @Override public Book[] newArray(int size) { return new Book[size]; } }; private Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } } 複製代碼
Book .java是一個表示圖書信息的類,實現了Parcelable接口。服務器
Book.aidl:markdown
package com.example.runningh.myapplication.aidl;
parcelable Book;
複製代碼
Book.aidl是Book類在AIDL中的聲明。app
IBookManager.aidl:ide
package com.example.runningh.myapplication.aidl; import com.example.runningh.myapplication.aidl.Book; import com.example.runningh.myapplication.aidl.IOnNewBookArrivedListener; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); } 複製代碼
IBookManager.adil是咱們定義的一個接口,裏面有兩個方法:getBookList和addBook,其中getBookList用於從遠程服務端獲取圖書列表,而addBook用於往圖書列表中添加一本書。oop
注意:在IBookManager.aidl中要手動導入Book類,不支持自動導入,這是AIDL的特殊之處。ui
經過執行build操做後,系統爲IBookManager.aidl生成的Binder類,在gen目錄下的source文件夾下可找到IBookManager.java類。this
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/RunningH/Documents/TencentGame/ThirdParty/ThemeSkinning/MyApplication2/app/src/main/aidl/com/example/runningh/myapplication/aidl/IBookManager.aidl */ package com.example.runningh.myapplication.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.example.runningh.myapplication.aidl.IBookManager { private static final java.lang.String DESCRIPTOR = "com.example.runningh.myapplication.aidl.IBookManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.runningh.myapplication.aidl.IBookManager interface, * generating a proxy if needed. */ public static com.example.runningh.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.aidl.IBookManager))) { return ((com.example.runningh.myapplication.aidl.IBookManager)iin); } return new com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.example.runningh.myapplication.aidl.Book _arg0; if ((0!=data.readInt())) { _arg0 = com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.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; } @Override public java.util.List<com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.example.runningh.myapplication.aidl.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); public java.util.List<com.example.runningh.myapplication.aidl.Book> getBookList() throws android.os.RemoteException; public void addBook(com.example.runningh.myapplication.aidl.Book book) throws android.os.RemoteException; } 複製代碼
首先這個類聲明瞭兩個方法getBookList和addBook,顯然這就是咱們在IBookMananger.aidl中所聲明的方法,同時它還聲明瞭兩個整型的id分別用於標識這兩個方法,這兩個id用於標識在transact過程當中客戶端所請求的究竟是哪一個方法。接着,它聲明瞭一個內部類Stub,這個Stub就是一個Binder類,當客戶端和服務端都位於同一個進程時,方法調用不會跨進程的transact過程,而當二者位於不一樣進程時,方法調用須要走transact過程,這個邏輯由Stub的內部代理Proxy來完成。
咱們看一下怎麼使用這個類,經過調用過程來熟悉其中的方法。
咱們新建一個工程並在新建BookManagerActivity、BookManagerService並將BookManagerService放在另一個進程中(在manifest中聲明)。在BookManagerActivity經過BindService的方式和BookManagerService綁定。
package com.example.runningh.myapplication.aidl; import android.app.Activity; import android.app.ListActivity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.annotation.Nullable; import android.util.Log; import com.example.runningh.myapplication.R; import java.util.List; /** * Created by RunningH on 2017/11/26. */ public class BookManagerActivity extends Activity { private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager iBookManager = IBookManager.Stub.asInterface(service); //注意這裏調用了Stub的asInterface方法,經過服務端的IBinder對象找到 try { List<Book> list = iBookManager.getBookList(); Log.i("ABC", "query book list, list type:" + list.getClass().getCanonicalName()); Log.i("ABC", "query book list:" + list.toString()); Book newBook = new Book(3, "HTML"); iBookManager.addBook(newBook); Log.i("ABC", "add book:" + newBook); List<Book> newList = iBookManager.getBookList(); Log.i("ABC", "query book list:" + newList.toString()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book_manager); Intent intent = new Intent(this, BookManagerService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); } } 複製代碼
BookManagerService.java:
package com.example.runningh.myapplication.aidl; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * Created by RunningH on 2017/11/26. */ public class BookManagerService extends Service { private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); private Binder mBinder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } }; @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1, "Android")); mBookList.add(new Book(2, "IOS")); } @Override public IBinder onBind(Intent intent) { return mBinder; } } 複製代碼
public static com.example.runningh.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.aidl.IBookManager))) { return ((com.example.runningh.myapplication.aidl.IBookManager)iin); } return new com.example.runningh.myapplication.aidl.IBookManager.Stub.Proxy(obj); } 複製代碼
private static class Proxy implements com.example.runningh.myapplication.aidl.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; } @Override public java.util.List<com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.example.runningh.myapplication.aidl.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(); } } } 複製代碼
@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.example.runningh.myapplication.aidl.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.example.runningh.myapplication.aidl.Book _arg0; if ((0!=data.readInt())) { _arg0 = com.example.runningh.myapplication.aidl.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } 複製代碼
總結:上述就是Binder的工做機制,咱們須要注意的是當客戶端發起遠程請求時,因爲當前線程會被掛起直至服務器端進程返回數據,因此若是一個遠程方法是很耗時的,那麼不能在UI線程中發起遠程請求;其次因爲服務端的Binder方法運行在Binder的線程池中,因此Binder方法無論是否耗時都是應該採用同步的方法去實現,由於它已經運行在一個線程中了。
參考:這篇文章是對《Android開發藝術探索》中IPC機制一章中的Binder內容進行的總結。這本書的做者任玉剛是一個大牛,這本書也是很通俗易懂,對Android開發的進階有很是大的幫助。