1. Binder簡介
Binder是Android中的一個類,它實現(xiàn)了IBinder接口。從IPC角度來說,Binder是Android中的一種跨進程通信方式。Binder還可以理解為一種虛擬的物理設(shè)備,其設(shè)備驅(qū)動是/dev/binder,該通信方式在Linux中沒有。從Android Framework(框架層) 角度來說,Binder是ServiceManager連接各種Manager(ActivityManager,WindowManager,等等)和相應(yīng)的ManagerService的橋梁;從android應(yīng)用層來講,Binder是客戶端和服務(wù)端進行通信的媒介,當bindService的時候,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù),包括普通服務(wù)和AIDL的服務(wù)。
Android開發(fā)中,Binder主要用于在Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及進程間通信,所以比較簡單,無法觸及Binder核心,而Messenger(信息員)的底層其實是AIDL,所以這里選擇用AIDL來分析Binder的工作機制。
2. 普通Service實現(xiàn)
public class BookService extends Service {
BookBinder bookBinder;
@Override
public void onCreate() {
super.onCreate();
bookBinder = new BookBinder();
}
@Override
public IBinder onBind(Intent intent) {
return bookBinder;
}
// service里的方法,這里是private,對外提供接口BookListenner
private class BookBinder extends Binder implements BookListnner {
@Override
public void bookPrint() {
Log.e("aaa", "book print");
}
}
}
public interface BookListnner {
void bookPrint();
}
activity中綁定service并調(diào)用service中的方法
public class BookActivity extends AppCompatActivity {
BookListnner bookListnner;
ServiceConnection serviceConnection;
Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
intent = new Intent(this, BookService.class);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 綁定成功后
if (service != null) {
bookListnner = (BookListnner) service;
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 解綁成功后
}
};
// 綁定服務(wù)
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bookListnner.bookPrint();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解綁兩次也會出錯,這里trycatch包一下
try {
unbindService(serviceConnection);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 啟動服務(wù)-startService
服務(wù)生命周期:onCreate - 停止服務(wù)-stopService
服務(wù)生命周期:onDestroy - 綁定服務(wù)-bindService
服務(wù)生命周期:onCreate-onBind - 解綁服務(wù)-unbindService
服務(wù)生命周期:onUnBind-onDestroy - 如果只是綁定服務(wù),Activity銷毀時要解綁服務(wù)。
- 如果啟動服務(wù)和綁定服務(wù)都執(zhí)行了,Activity銷毀時,也要解綁服務(wù),這時因為服務(wù)被startService啟動過,不會隨著解綁而銷毀。除非顯性執(zhí)行stopService,否則一直在后臺運行。
- 服務(wù)調(diào)用Activity方法使用廣播方式。
3. 編寫B(tài)ook.java,Book.aidl,IBookManager.aidl文件
public class Book implements Parcelable {
private int bookId;
private String bookName;
// getter setter parcelable...
}
// Book.aidl
package qingfengmy.developmentofart._2activity.aidl;
parcelable Book;
- Book.aidl文件和Book.java要在同一目錄下。
- 用Android studio新建的aidl要求interface的名字唯一,因為之前已經(jīng)建過Book.java,所以Book.aidl無法創(chuàng)建,可以先寫成IBook.aidl,然后再重命名。
- 新建的aidl文件是帶interface的,注意這里的Book.aidl沒有interface聲明。
- Book.aidl是Book類在aidl中的聲明,這樣別的aidl類就可以引用它。
// IBookManager.aidl
package qingfengmy.developmentofart._2activity.aidl;
// Declare any non-default types here with import statements
import qingfengmy.developmentofart._2activity.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
- IBookManager.aidl和Book.aidl在同一目錄下。
- IBookManager.aidl引用了Book,盡管同一目錄,也要寫import語句,否則報錯如下:
> java.lang.RuntimeException:
com.android.ide.common.process.ProcessException:
org.gradle.process.internal.ExecException:
Process 'command 'F:\android-sdk\build-tools\23.0.3\aidl.exe'' finished with non-zero exit value 1
- Book.aidl沒有對應(yīng)的java文件生成。
- IBookManager.aidl有對應(yīng)的java文件生成其路徑是
F:\workspace4sdutio\DevelopmentOfArt
\app\build\generated\source\aidl\debug
\qingfengmy\developmentofart\_2activity\aidl\IBookManager.java
4. aidl生成的java文件分析
public interface IBookManager extends android.os.IInterface {
//...
}
IBookManager是個interface,并且實現(xiàn)了IInterface接口。所有可以在Binder中傳輸?shù)慕涌诙夹枰^承interface這個接口。
public java.util.List<qingfengmy.developmentofart._2activity.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(qingfengmy.developmentofart._2activity.aidl.Book book) throws android.os.RemoteException;
定義了兩個方法,就是我們在aidl中定義的方法。
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
聲明了兩個int型的方法ID,這兩個id用于標識在transact過程中客戶端所請求的到底是哪個方法。
public static abstract class Stub extends android.os.Binder implements qingfengmy.developmentofart._2activity.aidl.IBookManager {
...
}
一個叫做Stub的內(nèi)部類,這個Stub(存根)就是一個Binder類,當客戶端和服務(wù)端位于同一進程時,方法調(diào)用不會走跨進程的transact過程,而當兩者位于不同進程時,方法調(diào)用需要走transact過程,這個邏輯由Stub內(nèi)部代理Proxy來完成。
private static final java.lang.String DESCRIPTOR = "qingfengmy.developmentofart._2activity.aidl.IBookManager";
DESCRIPTOR:Binder的唯一標識,一般用當前Binder的類名表示,比如本例中的"qingfengmy.developmentofart._2activity.aidl.IBookManager";
public static qingfengmy.developmentofart._2activity.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof qingfengmy.developmentofart._2activity.aidl.IBookManager))) {
return ((qingfengmy.developmentofart._2activity.aidl.IBookManager) iin);
}
return new qingfengmy.developmentofart._2activity.aidl.IBookManager.Stub.Proxy(obj);
}
用于將服務(wù)端的Binder對象轉(zhuǎn)換成客戶端所需的aidl接口類型的對象,這種轉(zhuǎn)換過程是區(qū)分進程的,如果客戶端和服務(wù)端位于同一進程,那么此方法返回的就是服務(wù)端的Stub對象本身,否則返回的就是系統(tǒng)封裝后的Stub.proxy對象。
@Override
public android.os.IBinder asBinder() {
return this;
}
返回當前的Binder對象,IInterface接口中的唯一方法。
@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<qingfengmy.developmentofart._2activity.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
qingfengmy.developmentofart._2activity.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = qingfengmy.developmentofart._2activity.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
這個方法運行在服務(wù)端中的線程池中,當客戶端發(fā)起跨進程請求時,遠程請求會通過系統(tǒng)底層封裝后交由此方法來處理。該方法原型為
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
服務(wù)端通過code可以確定客戶端所請求的目標方法是什么,接著從data中取出目標方法所需的參數(shù),然后執(zhí)行目標方法。當目標方法執(zhí)行完畢后,就向reply中寫入返回值。
如果此方法返回false,那么客戶端會請求失敗,因此我們可以利用這個特性來做權(quán)限驗證,畢竟我們不希望隨便一個進程都能遠程調(diào)用我們的服務(wù)。
private static class Proxy implements qingfengmy.developmentofart._2activity.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;
}
}
Proxy是Stub的內(nèi)部類,如果是跨進程的話,客戶端拿到的Binder會是proxy。這里的方法在客戶端調(diào)用。
private static class Proxy implements qingfengmy.developmentofart._2activity.aidl.IBookManager {
@Override
public java.util.List<qingfengmy.developmentofart._2activity.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<qingfengmy.developmentofart._2activity.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(qingfengmy.developmentofart._2activity.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
getBookList方法所需要的輸入型Parcel對象_data,輸出型Parcel對象_reply和返回值對象List;然后把該方法的參數(shù)信息寫入_data中;接著調(diào)用transact方法來發(fā)起RPC(遠程過程調(diào)用)請求,同時當前線程掛起;然后服務(wù)端的onTransact方法會被調(diào)用,知道RPC過程返回后,當前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過程的返回結(jié)果;最后返回_reply中的數(shù)據(jù)。
@Override
public void addBook(qingfengmy.developmentofart._2activity.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();
}
}
這個方法也在客戶端運行,同上面的方法。只是addBook沒有返回值所以不需要從_reply中取結(jié)果。
注意:首先,當客戶端發(fā)起遠程請求時,由于當前線程會被掛起直至服務(wù)器進程返回數(shù)據(jù),所以如果一個遠程方法是很耗時的,那么不能在UI線程中發(fā)起此遠程請求;其次,由于服務(wù)端的Binder方法運行在Binder的線程池中,所以Binder方法不管是否耗時都應(yīng)該采用同步的方式去實現(xiàn),因為它已經(jīng)運行在一個線程中了。
5. 同一個進程客戶端調(diào)用服務(wù)端
public class BookService extends Service {
BookBinder bookBinder;
@Override
public void onCreate() {
super.onCreate();
bookBinder = new BookBinder();
Log.e("aaa", "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.e("aaa", "onBind");
return bookBinder;
}
private class BookBinder extends IBookManager.Stub {
@Override
public List<Book> getBookList() throws RemoteException {
Log.e("aaa", "getBookList");
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
Log.e("aaa", "addBook");
}
}
}
public class BookActivity extends AppCompatActivity {
IBookManager iBookManager;
ServiceConnection serviceConnection;
Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
intent = new Intent(this, BookService.class);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (service != null) {
iBookManager = IBookManager.Stub.asInterface(service);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
iBookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解綁兩次也會出錯,這里trycatch包一下
try {
unbindService(serviceConnection);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6. 客戶端和服務(wù)端不在一個進程
首先把BookActivity的進程改成子進程
<activity android:name="._2activity.BookActivity" android:process=":remote"></activity>
結(jié)果正常執(zhí)行。
接著改成全局進程。
<activity android:name="._2activity.BookActivity" android:process="qingfengmy.developmentofart.remote"></activity>
也可以??赡苁且驗轱@性綁定服務(wù)的原因,這里改成隱式綁定服務(wù)。
<service android:name="._2activity.BookService">
<intent-filter>
<action android:name="qingfengmy.developmentofart.bookService"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</service>
intent = new Intent();
intent.setAction("qingfengmy.developmentofart.bookService");
綁服務(wù)的時候直接掛掉。報錯如下:
Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit
這里說的是不能隱式啟動Service,這是5.0以后加上的,那么我們修改如下:
intent = new Intent();
intent.setAction("qingfengmy.developmentofart.bookService");
intent.setPackage("qingfengmy.developmentofart");
結(jié)果無論BookActivity的進程改成什么,都可以成功鏈接service并調(diào)用service的方法。
7. 跨應(yīng)用調(diào)用該服務(wù)
首先Book和aidl的包路徑要和服務(wù)里的一樣
qingfengmy.developmentofart._2activity.aidl.Book
qingfengmy/developmentofart/_2activity/aidl/Book.aidl
qingfengmy/developmentofart/_2activity/aidl/IBookManager.aidl
其他和同應(yīng)用的寫法一樣。原service沒做任何其他配置。至于為什么Service沒有配置android:exported="true"也可以被其他應(yīng)用啟動。那是因為exported屬性,在四大組件沒有配置intent-filter時,其值默認為false。在四大組件配置intent-filter時,其值默認為true。也就是上面的實現(xiàn),隱式啟動Service時,exported的值都是true。如果改成false,跨應(yīng)用將不可調(diào)用。而同應(yīng)用里的不同進程,還是可以啟動和綁定Service。