前言
Binder是Android的一個(gè)類嘁字,它實(shí)現(xiàn)了IBinder接口码倦。從IPC角度來(lái)說(shuō)咽块,Binder是Android中的一種跨進(jìn)程通信方式忽洛;從Android應(yīng)用層來(lái)說(shuō)腻惠,Binder是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService的時(shí)候欲虚,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對(duì)象集灌,通過(guò)這個(gè)Binder對(duì)象,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)复哆,這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)欣喧。
在Android開發(fā)中,Binder主要用在Service中梯找,包括AIDL(Android Interface Defination Language)和Messenger唆阿,其中普通Service中的Binder不涉及進(jìn)程間通信,所以較為簡(jiǎn)單锈锤,無(wú)法觸及Binder的核心驯鳖,而Messenger的底層其實(shí)是AIDL,所以這里選擇AIDL來(lái)分析Binder的工作機(jī)制久免。
使用示例
新建三個(gè)文件浅辙,Book.java、Book.aidl和IBookManager.aidl阎姥,代碼如下:
Book.java
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是一個(gè)表示圖書信息的類记舆,實(shí)現(xiàn)了Parcelable接口。
Book.aidl:
package com.example.runningh.myapplication.aidl;
parcelable Book;
Book.aidl是Book類在AIDL中的聲明呼巴。
IBookManager.aidl:
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是我們定義的一個(gè)接口泽腮,里面有兩個(gè)方法:getBookList和addBook,其中g(shù)etBookList用于從遠(yuǎn)程服務(wù)端獲取圖書列表衣赶,而addBook用于往圖書列表中添加一本書盛正。
注意:在IBookManager.aidl中要手動(dòng)導(dǎo)入Book類,不支持自動(dòng)導(dǎo)入屑埋,這是AIDL的特殊之處豪筝。
通過(guò)執(zhí)行build操作后,系統(tǒng)為IBookManager.aidl生成的Binder類摘能,在gen目錄下的source文件夾下可找到IBookManager.java類续崖。
/*
* 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;
}
首先這個(gè)類聲明了兩個(gè)方法getBookList和addBook,顯然這就是我們?cè)贗BookMananger.aidl中所聲明的方法团搞,同時(shí)它還聲明了兩個(gè)整型的id分別用于標(biāo)識(shí)這兩個(gè)方法严望,這兩個(gè)id用于標(biāo)識(shí)在transact過(guò)程中客戶端所請(qǐng)求的到底是哪個(gè)方法。接著逻恐,它聲明了一個(gè)內(nèi)部類Stub像吻,這個(gè)Stub就是一個(gè)Binder類峻黍,當(dāng)客戶端和服務(wù)端都位于同一個(gè)進(jìn)程時(shí),方法調(diào)用不會(huì)跨進(jìn)程的transact過(guò)程拨匆,而當(dāng)兩者位于不同進(jìn)程時(shí)姆涩,方法調(diào)用需要走transact過(guò)程,這個(gè)邏輯由Stub的內(nèi)部代理Proxy來(lái)完成惭每。
我們看一下怎么使用這個(gè)類骨饿,通過(guò)調(diào)用過(guò)程來(lái)熟悉其中的方法。
我們新建一個(gè)工程并在新建BookManagerActivity台腥、BookManagerService并將BookManagerService放在另外一個(gè)進(jìn)程中(在manifest中聲明)宏赘。在BookManagerActivity通過(guò)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); //注意這里調(diào)用了Stub的asInterface方法黎侈,通過(guò)服務(wù)端的IBinder對(duì)象找到
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;
}
}
- BookManagerService中new了一個(gè)Stub對(duì)象察署,并重寫addBook和getBookList方法。這樣Stub的構(gòu)造方法被調(diào)用峻汉,構(gòu)造方法中調(diào)用了Binder的attachInterface將該Binder對(duì)象(Stub就是一個(gè)Binder對(duì)象)和DESCRIPTOR(Binder的唯一標(biāo)識(shí)贴汪,一般用Binder的類名表示)聯(lián)系起來(lái)。
- 上面生成的Stub對(duì)象(也就是Binder對(duì)象)通過(guò)BinderService的方式傳遞給客戶端俱济,也就是BookManagerActivity的mConnection的onServiceConnected方法中service對(duì)象嘶是。通過(guò)調(diào)用IBookManager.Stub.asInterface方法得到客戶端所需要的AIDL接口類型對(duì)象钙勃。這是區(qū)分進(jìn)程的蛛碌,如果客戶端和服務(wù)端處于同一個(gè)進(jìn)程,那么執(zhí)行queryLocalInterface(DESCRIPTOR)返回就不為空(還記得上面生成Stub對(duì)象的時(shí)候?qū)⒃搶?duì)象和DESCRIPTOR綁定嗎辖源?這里就是取出來(lái))蔚携,所以此方法返回的就是服務(wù)端的Stub對(duì)象本身。如果是不同的進(jìn)程則返回的是系統(tǒng)封裝或的Stub.proxy對(duì)象克饶。對(duì)應(yīng)于下面的則是new了一個(gè)Stub.Proxy對(duì)象酝蜒。
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);
}
- 先看如果處于同一個(gè)進(jìn)程的情況,得到服務(wù)端的Stub對(duì)象后矾湃,直接調(diào)用getBookList和addBook方法亡脑。因?yàn)镾tub對(duì)象實(shí)現(xiàn)了IBookManager接口,并實(shí)現(xiàn)了其中的getBookList和addBook方法邀跃,所以這里實(shí)際調(diào)用的是getBookList和addBook方法霉咨。這樣就實(shí)現(xiàn)了客戶端和服務(wù)端的交互。
- 再來(lái)看不同進(jìn)程的情況拍屑,我們得到的是一個(gè)Stub.Proxy對(duì)象途戒。在客戶端調(diào)用getBookList和addBook方法,實(shí)際上調(diào)用的是Stub.Proxy對(duì)象的getBookList和addBook方法僵驰。
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();
}
}
}
- getBookList方法中調(diào)用了mRemote.transact方法喷斋,第一個(gè)參數(shù)標(biāo)識(shí)調(diào)用哪一個(gè)方法唁毒,第二個(gè)參數(shù)標(biāo)識(shí)需要傳遞進(jìn)去的參數(shù),第三個(gè)參數(shù)標(biāo)識(shí)需要返回的數(shù)據(jù)星爪,第四個(gè)參數(shù)一般為0浆西。調(diào)用transact方法發(fā)起RPC(遠(yuǎn)程過(guò)程調(diào)用)請(qǐng)求,同時(shí)當(dāng)前線程掛起(等待數(shù)據(jù)返回)移必,然后服務(wù)端的onTransact方法會(huì)被調(diào)用室谚,直到RPC過(guò)程返回后,當(dāng)前線程繼續(xù)執(zhí)行崔泵,并從_reply中取出RPC過(guò)程的返回結(jié)果秒赤。
- addBook方法調(diào)用了mRemote.transact方法,參數(shù)的意思同上一樣憎瘸。由于addBook不需要返回值入篮,所以它不需要從_reply中取出返回值。
- 上面兩個(gè)方法都調(diào)用了transact方法幌甘,那么該方法被調(diào)用后會(huì)觸發(fā)服務(wù)端的Stub對(duì)象的onTransact方法被調(diào)用潮售。
@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);
}
- 通過(guò)onTransact方法,我們通過(guò)code參數(shù)作為標(biāo)識(shí)具體調(diào)用的哪一個(gè)方法锅风,比如TRANSACTION_getBookList就是調(diào)用getBookList方法酥诽,這里做的操作是調(diào)用Stub對(duì)象的getBookList方法并且將結(jié)果寫入到reply(如果該操作需要結(jié)果返回的話)中然后返回。
總結(jié):上述就是Binder的工作機(jī)制皱埠,我們需要注意的是當(dāng)客戶端發(fā)起遠(yuǎn)程請(qǐng)求時(shí)肮帐,由于當(dāng)前線程會(huì)被掛起直至服務(wù)器端進(jìn)程返回?cái)?shù)據(jù),所以如果一個(gè)遠(yuǎn)程方法是很耗時(shí)的边器,那么不能在UI線程中發(fā)起遠(yuǎn)程請(qǐng)求训枢;其次由于服務(wù)端的Binder方法運(yùn)行在Binder的線程池中,所以Binder方法不管是否耗時(shí)都是應(yīng)該采用同步的方法去實(shí)現(xiàn)忘巧,因?yàn)樗呀?jīng)運(yùn)行在一個(gè)線程中了恒界。
參考:這篇文章是對(duì)《Android開發(fā)藝術(shù)探索》中IPC機(jī)制一章中的Binder內(nèi)容進(jìn)行的總結(jié)。這本書的作者任玉剛是一個(gè)大牛砚嘴,這本書也是很通俗易懂十酣,對(duì)Android開發(fā)的進(jìn)階有非常大的幫助。