Binder實現了IBinder接口,一般咱們不會直接去實現Binder,而是經過aidl工具來定義接口,自動生成相應的Binder的子類。java
從Android Framework角度來講,Binder是ServiceManager鏈接各類Manager(ActivityManager,WindowManager,等等)和相應ManagerService的橋樑。android
從Android應用層來講,Binder是客戶端和服務端進行通訊的媒介,經過bindService,服務端會返回一個Binder對象。同過這個Binder對象,客戶端就能夠獲取服務端提供的服務或數據。併發
咱們經過一個簡單的AIDL示例來學習Binder的使用。app
AIDL示例:
目錄結構ide
Book.java:
package com.weijie.aidlapplication.aidl;工具
import android.os.Parcel;
import android.os.Parcelable;學習
/**
* Created by weijie on 17-3-8.
*/this
public class Book implements Parcelable {
public int bookId;
public String bookName;spa
@Override
public int describeContents() {
return 0;
}.net
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.bookId);
dest.writeString(this.bookName);
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
this.bookId = in.readInt();
this.bookName = in.readString();
}
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];
}
};
}
Book.aidl:
package com.weijie.aidlapplication.aidl;
parcelable Book;
IBookManager.aidl
// IBookManager.aidl
package com.weijie.aidlapplication.aidl;
// Declare any non-default types here with import statements
import com.weijie.aidlapplication.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
注意:
1. Android Studio中,新建AIDL文件:File->New->AIDL
2. Book.aidl和IBookManager.aidl位於相同的包中,可是在IBookManager中仍然要導入Book類。
IBookManager.java
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /home/weijie/AndroidStudioProjects/AidlApplication/app/src/main/aidl/com/weijie/aidlapplication/aidl/IBookManager.aidl
*/
package com.weijie.aidlapplication.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.weijie.aidlapplication.aidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.weijie.aidlapplication.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.weijie.aidlapplication.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.weijie.aidlapplication.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.weijie.aidlapplication.aidl.IBookManager))) {
return ((com.weijie.aidlapplication.aidl.IBookManager)iin);
}
return new com.weijie.aidlapplication.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.weijie.aidlapplication.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.weijie.aidlapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.weijie.aidlapplication.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.weijie.aidlapplication.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.weijie.aidlapplication.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.weijie.aidlapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.weijie.aidlapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.weijie.aidlapplication.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.weijie.aidlapplication.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.weijie.aidlapplication.aidl.Book book) throws android.os.RemoteException;
}
IBookManager這個類是系統自動生成的,AS中點擊」Make Project」便可生成。
IBookManager繼承了IInterface接口,同時自己也是一個接口。
全部能夠在Binder中傳輸的接口都須要繼承IInterface接口。
IBookManager包括:
聲明瞭getBookList和addBook兩個方法
聲明瞭兩個整型的id分別用於標識這兩個方法
聲明瞭一個內部類Stub,這個Stub就是一個Binder類。當客戶端和服務端位於不一樣進程時,方法須要走transact過程,這個邏輯由Stub的內部代理類Proxy完成。
DESCRIPTOR
Binder的惟一標示,通常用當前Binder的類名錶示。
asInterface(android.os.IBinder obj)
用於將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象。
這種轉換是區分進程的,若是客戶端和服務端位於同一進程,那麼返回的是服務端的Stub對象自己,不然返回的是系統封裝後的Stub.proxy對象。
咱們來看這個示例:
public class LocalService extends Service {
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
}
public void onServiceConnected(ComponentName name, IBinder service) {
// 1. 同一進程
mIMyService = ((LocalBinder.LocalBinder)service).getService;
// 2. 同一進程或不一樣進程
mIMyService = IMyService.Stub.asInterface(service);
}
當客戶端和服務端在同一個進程時,能夠直接將IBinder接口轉型爲本地自定義Binder,而後經過自定義Binder的getService方法拿到IMyService接口實例。
asInterface的方法適用性更廣,若是客戶端和服務端位於同一進程,那麼返回的是服務端的Stub對象自己,不然返回的是系統封裝後的Stub.proxy對象。推薦使用這一種方法。
asBinder
返回當前的Binder對象
onTransact(int code, android.os.Parcel data, android.os.Parcel reply,int flags)
這個方法運行在服務端的Binder線程池中,當客戶端發起跨進程請求時,調用該方法。
服務端經過code判斷客戶端請求的方法,接着從data中取出目標方法所需的參數(if have),而後執行目標方法。
當目標方法執行完畢後,就向reply中寫入返回值(if have)。
若是此方法返回false,那麼客戶端的請求會失敗。
Proxy#getBookList
這個方法運行在客戶端,它的內部實現是這樣的:
Start
建立輸入型Parcel對象_data
建立輸出型Parcel對象_reply
建立返回值對象List
寫入_data
調用transact方法發起RPC(遠程過程調用),同時當前線程掛起
調用服務端的onTransact方法直到RPC過程返回後,當前線程繼續執行
從_reply中取數據
End
Proxy#addBook
這個方法的執行過程和getBookList是同樣的,addBook沒有返回值,因此它不須要從_reply中取出返回值。
注意:
當客戶端發起遠程請求時,因爲當前線程會被掛起直至服務端進程返回數據,因此若是一個遠程方法是很耗時的,那麼不能在UI線程中發起此遠程請求。
因爲服務端的Binder方法運行在Binder的線程池中,因此Binder方法無論是否耗時都應該採用同步的方式去實現。
AIDL支持的數據類型:
基本數據類型(int,long,char,boolean,double)
String和CharSequence
List:只支持ArrayList,裏面每一個元素都必須被AIDL支持
Map:只支持HashMap,裏面的每一個元素都必須被AIDL支持,包括key和value
Parcelabel:全部實現了Parcelable接口的對象,必須顯示import進來
AIDL:必須顯示import進來
若是AIDL文件中用到了自定義的Parcelable對象,那麼必須新建一個和它同名的AIDL文件,並在其中聲明它爲Parcelable類型,如上面的Book.aidl。
AIDL中除了基本數據類型,其它類型的參數必須標上方向:in,out或者inout。
AIDL接口中只支持方法,不支持聲明靜態常量,這一點區別於傳統的接口。
BookManagerService.java
public class BookManagerService extends Service {
private static final String TAG = "BMS";
// CopyOnWriteArrayList支持併發讀/寫,自動進行線程同步
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
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);
}
};
public BookManagerService() {
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
}
雖然服務端返回的是CopyOnWriteArrayList,可是在Binder中會按照List的規範去訪問數據並最終造成一個新的ArrayList傳遞給客戶端。
AndroidManifest.xml
註冊BookManagerService
<service
android:name=".aidl.BookManagerService"
android:enabled="true"
android:process=":remote"
android:exported="true"></service>
客戶端實現
public class MainActivity extends AppCompatActivity {
public static final String TAG = "weijie";
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
List<Book> list = bookManager.getBookList();
Log.d(TAG, "onServiceConnected: query book list, list type: " + list.getClass().getCanonicalName());
Log.d(TAG, "onServiceConnected: query book list: " + list.toString());
Book newBook = new Book(3, "Android 開發藝術探索");
Log.d(TAG, "onServiceConnected: add book: " + newBook);
bookManager.addBook(newBook);
List<Book> newList = bookManager.getBookList();
Log.d(TAG, "onServiceConnected: query book list: " + newList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); } } 輸出結果 01-02 09:47:15.101 5504 5504 D weijie : onServiceConnected: query book list, list type: java.util.ArrayList 01-02 09:47:15.102 5504 5504 D weijie : onServiceConnected: query book list: [Id = 1, Name = Android, Id = 2, Name = Ios] 01-02 09:47:15.102 5504 5504 D weijie : onServiceConnected: add book: Id = 3, Name = Android 開發藝術探索 01-02 09:47:15.105 5504 5504 D weijie : onServiceConnected: query book list: [Id = 1, Name = Android, ---------------------