一:aidl與messenger的使用區(qū)別
- aidl與messenger都是使用在進程間通信的方式兼蜈,底層都是Binder锭吨。不是進程間通信的可以直接使用Binder。
- messenger底層使用的是aidl結(jié)構(gòu)。
- messenger服務(wù)端串行執(zhí)行任務(wù)。
- aidl可以實現(xiàn)并發(fā)操作好啰,但是要注意線程安全。aidl還可以調(diào)用遠程服務(wù)的方法儿奶,messenger則不可以框往。
- aidl的同步主要是在aidl接口在service中的實現(xiàn)時添加。
二:一個簡單的aidl創(chuàng)建流程
(一):創(chuàng)建aidl文件
image.png
(二):創(chuàng)建遠程服務(wù)
注意aidl接口的方法闯捎,是需要線程安全的椰弊。
image.png
(三):將遠程的aidl包拷貝到本地項目
后面還會介紹如何使用parcelable對象,其中實現(xiàn)parcelable對象的類瓤鼻,也需要寫一個aidl文件秉版。
(四):綁定遠程服務(wù)
在調(diào)用aidl接口方法的時候,需要注意這個方法是耗時操作茬祷。
image.png
三:遠程啟動服務(wù)的注意事項
aidl一般是用在兩個進程中清焕,如果使用在兩個app中,那么要怎么從一個app啟動另一個app的遠程服務(wù)呢祭犯?在android 5.0之后秸妥,系統(tǒng)規(guī)定要使用顯示啟動所有的service,否則就會拋出異常盹憎。因此就有下面兩種啟動方式
(一)設(shè)置過濾器筛峭,google并不建議我們這么做
java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.xia }
<service android:name=".MyService"
>
<intent-filter>
<action android:name="com.xia"/>
</intent-filter>
</service>
intent.setAction("com.xia");
intent.setPackage("xiaguangcheng.com.local");
由于service設(shè)置了過濾器铐刘,那么exported就默認(rèn)為true了陪每。
(二)使用全類名打開service,主要要把允許其他app使用設(shè)置為true
java.lang.SecurityException: Not allowed to bind to service Intent { cmp=xiaguangcheng.com.local/.MyService }
<service android:name=".MyService"
android:exported="true">
</service>
//這里的pkg應(yīng)該是遠程服務(wù)的applicationId, 然后是遠程服務(wù)的全路徑名
intent.setComponent(new ComponentName("xiaguangcheng.com.local","xiaguangcheng.com.local.MyService"));
由于沒有設(shè)置過濾器镰吵,因此需要顯示指定exproted=true檩禾。
四:創(chuàng)建一個略復(fù)雜的aidl
(一)aidl支持的數(shù)據(jù)類型
- 基本數(shù)據(jù)類型
- String和CharSequence
- ArrayList,元素必須被aidl支持
- HashMap疤祭,元素必須被aidl支持
- Parcelable盼产,所有實現(xiàn)了Parcelable接口的對象
- aidl接口本身也能在aidl文件中使用
(二)使用parcelable接口的對象
- 我們要在java包中創(chuàng)建一個Book.java文件實現(xiàn)parcelable接口
- 我們還要在aidl包中創(chuàng)建一個同名的aidl文件Book.aidl,并在其中聲明
parcelable Book;
同時手動導(dǎo)入Book.java的包名勺馆。
(三)使用RemoteCallbackList刪除跨進程接口
public class BookManagerService extends Service {
public static final String TAG="BMS";
//讀寫分離戏售,最終一致性,保障并發(fā)安全
private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>();
private AtomicBoolean mIsServiceDestoryed=new AtomicBoolean(false);
//解決因為aidl的序列化問題草穆,導(dǎo)致傳遞過來的對象灌灾,已非客戶端的對象。這個類則解決了這個問題悲柱。
private RemoteCallbackList<IBookManagerNewBookArravied> mListenerList=
new RemoteCallbackList<>();
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book("西游記",111));
mBookList.add(new Book("紅樓夢",222));
new Thread(new ServiceWorker()).start();
}
private Binder mBinder=new IBookManager.Stub(){
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void registerListener(IBookManagerNewBookArravied listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unRegisterListener(IBookManagerNewBookArravied Listener) throws RemoteException {
mListenerList.unregister(Listener);
}
};
public BookManagerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while(!mIsServiceDestoryed.get()){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId= mBookList.size()*111;
Book newBook=new Book("bookName:"+bookId,bookId);
try {
onNewBookArrrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
private void onNewBookArrrived (Book newBook)throws RemoteException {
mBookList.add(newBook);
final int N=mListenerList.beginBroadcast();
for(int i=0;i<N;i++){
IBookManagerNewBookArravied iBookManagerNewBookArravied = mListenerList.getBroadcastItem(i);
if(iBookManagerNewBookArravied!=null){
iBookManagerNewBookArravied.onNewBookArrived(newBook);
}
}
mListenerList.finishBroadcast();
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
}
五:如何判斷Binder連接是否中斷
在綁定服務(wù)完成之后锋喜,我們要給binder設(shè)置一個死亡代理。即通過IBinder.linkToDeath(mDeathRecipient,0)
來設(shè)置。其中這個死亡接收者有一個binderDied的回調(diào)嘿般。當(dāng)binder中斷段标,就需要在這個回調(diào)中,取消之前綁定死亡代理的那個binder炉奴,重新綁定服務(wù)逼庞,綁定死亡代理。
IM im;
private IBinder.DeathRecipient mDeathRecipient=new IBinder.DeathRecipient(){
@Override
public void binderDied() {
if(im==null){
return;
}
im.asBinder().unlinkToDeath(mDeathRecipient,0);
im=null;
connect();
}
};
……
public void connect(){
Intent intent=new Intent("com.abc");
intent.setPackage("com.xiaguangcheng.aidl");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
im=IM.Stub.asInterface(service);
try {
service.linkToDeath(mDeathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
},BIND_AUTO_CREATE);
}