[toc]
在了解AIDL之前,我們要對Binder有簡單了解。
Binder
Binder是android的一個類,實現(xiàn)了IBinder
接口。Binder主要用在Service中,包括AIDL和Messenger。Messenger底層是AIDL。
IPC角度
Binder是Android中一種跨進(jìn)程的通信方式,還可以理解為一種虛擬的物理設(shè)備,設(shè)備驅(qū)動為/dev/binder,在Linux中沒有這種通信方式。AndroidFramework
角度
Binder是ServiceManager
連接各種Manager(ActivityManager、WindowManager...)
和相應(yīng)ManagerService
的橋梁。Android應(yīng)用層
Binder是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService
時,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象,客戶端可以獲取服務(wù)端提供的服務(wù)或數(shù)據(jù)。服務(wù)包括普通服務(wù)和基于AIDL((Android Interface Definition Language)的服務(wù)。
Binder意外死亡后(服務(wù)端進(jìn)程意外停止)重新連接服務(wù)方法
-
1)給Binder設(shè)置
DeathRecipient
監(jiān)聽Binder提供了兩個配對方法,
linkToDeath
和unlinkToDeath
,linkToDeath
為Binder設(shè)置一個死亡代理,Binder死亡時,我們收到通知。(isBinderAlive
判斷是否死亡)Binder死亡時,系統(tǒng)會回調(diào)
binderDied()
方法,我們可以移除之前綁定的代理并重新綁定遠(yuǎn)程服務(wù)。 2)在
onServiceDisconnected
中重連遠(yuǎn)程服務(wù)
兩種方法區(qū)別:
-
onServiceDisconnected
在客戶端UI線程中被回調(diào) - binderDied方法中不能訪問UI
服務(wù)端——創(chuàng)建AIDL接口
AIDL文件支持的數(shù)據(jù)類型
- 基本數(shù)據(jù)類型(除short,因為序列化不支持short類型)
- String、CharSequence
- ArrayList,里面元素必須能夠被AIDL支持
- HashMap,里面元素必須能夠被AIDL支持
- Parcelable:所有實現(xiàn)了Parcelable接口的對象
- AIDL:AIDL接口本身也可在AIDL文件中使用
PS:
- AIDL除基本數(shù)據(jù)類型,其他類型參數(shù)必須標(biāo)上方向
- in:輸入型
- out:輸出型
- inout:輸入輸出型
- AIDL接口中只支持方法,不支持聲明靜態(tài)常量!區(qū)別于傳統(tǒng)接口
- 自定義parcelable對象和AIDL對象必須顯示import
- AIDL文件用到了自定義parcelable對象,必須新建一個和他同名的AIDL,在其中該聲明他為parcelable類型
IBookManager .aidl
package com.emma.www.myapplication;
import com.emma.www.myapplication.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Book.java
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = 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 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.emma.www.myapplication;
parcelable Book;
服務(wù)端——實現(xiàn)service
Android SDK工具生成一個.java接口文件用你的.aidl文件命名生成的接口包含一個名字為Stub
的子類,這是一個它父類的抽象實現(xiàn),并且聲明了.aidl中所有的方法。
Stub
也定義了一些輔助的方法,最顯著的就是asInterface()
,它是用來接收一個IBinder
(通常IBinder傳遞給客戶端的onServiceConnected()
回調(diào)方法)并且返回一個Stub接口的實例 。
// IService .aidl
interface IService {
String hello(String name);
}
繼承Service并且實現(xiàn)onBind()方法返回一個實現(xiàn)生成的Stub類
// AIDLService .java
public class AIDLService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return new IService.Stub() {
@Override
public String hello(String name) throws RemoteException {
// TODO Auto-generated method stub
return "hello"+name;
}
};
}
客戶端實現(xiàn)
public class MainActivity extends Activity {
IService RemoteService; //監(jiān)聽服務(wù)
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
Log.i("mConnection", service + "");
RemoteService = IService.Stub.asInterface(service);
try {
String s = RemoteService.hello("finch");
Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initService();
}
//連接服務(wù)
private void initService() {
Intent i = new Intent();
i.setAction("android.intent.action.AIDLService");
boolean ret = bindService(i, mConnection, Context.BIND_AUTO_CREATE);
}
//斷開服務(wù)
private void releaseService() {
unbindService(mConnection);
mConnection = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseService();
}
}
- 客戶端
遠(yuǎn)程方法比較耗時會導(dǎo)致客戶端線程阻塞,所以避免在客戶端UI線程中訪問遠(yuǎn)程方法。(onServiceConnected和onServiceDisconnected都運行在UI線程中,所以也不可以直接調(diào)用服務(wù)端耗時操作。)- 服務(wù)端
服務(wù)端本身運行在服務(wù)端的Binder線程池中,所以服務(wù)端本身就可以執(zhí)行大量耗時操作,不要在服務(wù)端重開線程執(zhí)行異步任務(wù)。
RemoteCallbackList
系統(tǒng)專門提供用于刪除跨進(jìn)程listener的接口,是一個泛型,支持管理任意的AIDL接口。
public class RemoteCallbackList<E extends IInterface>
工作原理
內(nèi)部有一個Map結(jié)構(gòu)用來保存所有的AIDL回調(diào)(key:Ibinder,value:Callback)Callback中封裝了真正的遠(yuǎn)程listener,客戶端注冊listener時,將listener信息存入mCallbacks.-
功能
- 多次跨進(jìn)程傳輸客戶端的同一個對象會在服務(wù)端生成不同對象,但這些對象底層的Binder對象是同一個。客戶端解注冊時,遍歷服務(wù)端所有l(wèi)istener,找到將其刪掉。
客戶端進(jìn)程終止后,可以自動移除客戶端注冊的listener
內(nèi)部實現(xiàn)了線程同步,注冊和解注冊時,不需要做額外的線程同步工作。
遍歷RemoteCallbackList
mListenerList.beginBroadcast()
和mListenerList.finishBroadcast
必須配對使用
權(quán)限驗證
- 在onBind中進(jìn)行驗證,失敗返回null
permission驗證
每個權(quán)限通過 protectionLevel 來標(biāo)識保護(hù)級別:
normal : 低風(fēng)險權(quán)限,申請就可以使用,安裝時不需要用戶確認(rèn)
dangerous:高風(fēng)險權(quán)限,安裝時需要用戶的確認(rèn)才可使用
signature:只有當(dāng)申請權(quán)限的應(yīng)用程序的數(shù)字簽名與聲明此權(quán)限的應(yīng)用程序的數(shù)字簽名相同時(如果是申請系統(tǒng)權(quán)限,則需要與系統(tǒng)簽名相同)才能將權(quán)限授給它
-
signatureOrSystem:簽名相同,或者申請權(quán)限的應(yīng)用為系統(tǒng)應(yīng)用
AndroidManifest中聲明權(quán)限Service的onBind中驗證
內(nèi)部應(yīng)用綁定服務(wù)進(jìn)行聲明權(quán)限即可~
<uses-permission
android:name="com.emma.www.myapplication.permission.ACCESS_BOOK_SERVICE"/>
-
通過服務(wù)端的onTransact方法中驗證,失敗返回false
AIDL oneway
oneway 主要有兩個特性:異步調(diào)用和串行化處理。
異步調(diào)用指應(yīng)用向 binder 驅(qū)動發(fā)送數(shù)據(jù)后不需要掛起線程等待 binder 驅(qū)動的回復(fù),而是直接結(jié)束。像一些系統(tǒng)服務(wù)調(diào)用應(yīng)用進(jìn)程的時候就會使用 oneway,比如 AMS 調(diào)用應(yīng)用進(jìn)程啟動 Activity,這樣就算應(yīng)用進(jìn)程中做了耗時的任務(wù),也不會阻塞系統(tǒng)服務(wù)的運行。
binder 協(xié)議
如果是 oneway 的話,客戶端就不需要掛起線程等待:
涉及到的 binder 命令也有規(guī)律,由外部發(fā)送給 binder 驅(qū)動的都是 BC_ 開頭,由 binder 驅(qū)動發(fā)往外部的都是 BR_開頭。
怎么理解客戶端線程掛起等待呢?有沒有實際占用 CPU 的調(diào)度?
這里的掛起相當(dāng)于 Thread 的 sleep,是真正的"休眠",底層調(diào)用的是 waiteventinterruptible()
Linux 系統(tǒng)函數(shù)。
waiteventinterruptible函數(shù)
Handle 中最關(guān)鍵的地方就是 Looper 的阻塞與喚醒,阻塞是調(diào)用了 nativePollOnce()
方法,當(dāng)時對它的底層實現(xiàn)感興趣,就去了解了一下,也學(xué)習(xí)到 Linux 用來實現(xiàn)阻塞/喚醒的 select、poll 和 epoll 機(jī)制。