前言:抱着最起碼的要求盡力去做好每一件事 ! ——秋不白
前言
Studio生成AIDL的代碼編寫,就不贅述了,要注意的是.aidl和對應的.java 文件的包名要保持一致。直蹦主題吧
本博客中有些細節問題忽略了,代碼中也註釋說明,在不考慮業務耗時的情況下,實現AIDL進程通信,數據更新監聽,註冊監聽器和移除監聽器,Binder死亡監聽,服務斷開連接重連解決方案。實現業務:監聽圖書新增變化。
支持數據類型
AIDL間通信比較簡單,支持的數據類型除了基礎數據類型,String CharSequence,其他定義的對象都要實現Parcelable,List只支持ArrayList,Map只支持HashMap,當然AIDL接口本身也是可以在AIDL文件中使用的。(實現AIDL中的數據更新監聽)
實現功能描述
1. 監聽器的添加和移除,數據監聽,數據更新監聽都是進程間傳遞數據的過程,對象傳遞的過程有個共同的特點,當然我們知道,對象是無法直接跨進程傳遞的,所以實現了Parcelable,進過序列化,反序列化後就不是同一個對象了。那如何實現移除監聽器呢?這些對象傳遞的過程中有什麼共同的特點,來實現移除對應的監聽器。
答案是:這些對象傳遞,多次跨進程傳輸客戶端的對象在服務端生成不同的對象,它們底層的Binder對象死同一個,利用這個特性,就可以實現移除監聽器。這裏就要使用到RemoteCallbackList,這不是List,內部實現了線程同步,可以簡單查看源碼都加上了同步鎖。
2.另外還使用到的CopyOnWriteArrayList,注意它不是繼承自ArrayList,同樣支持併發讀寫。使用這個來存放Book,被監聽的數據,前面提到只支持ArrayList,爲什麼呢,詳情看AidlService中代碼註釋。
3. 介紹第二種,服務斷開重連的解決方案。(未實踐,主要是理解這種方法) 使用CountDownLatch這個類,countDownLatch是一個計數器,線程完成一個記錄一個,計數器遞減,只能只用一次。(常用的是在onServiceDisconnected()中去處理服務重連的問題)。
文字描述結束
Activity(部分代碼,客戶端)
private ServiceConnection aidlConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iBookManager = IBookManager.Stub.asInterface(service);
}
/**
* 服務斷開重連解決方案一
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(aidlConn);
/** 移除新書到達監聽*/
try {
if (iBookManager != null) {
iBookManager.unregisterListener(mOnNewBookArrivedListener);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
Log.d("RedRose", " 新書已到達");
}
};
public void openAidl(View view) {
new Thread(new Runnable() {
@Override
public void run() {
connectBinderService();
}
}).start();
}
/**
* 服務斷開重連解決方案二
* 這部分代碼未實踐
*/
private synchronized void connectBinderService() {
/** 線程計數器*/
mConnectBinderCountDownLatch = new CountDownLatch(1);
Intent intent = new Intent(this, AidlService.class);
bindService(intent, aidlConn, BIND_AUTO_CREATE);
try {
mConnectBinderCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private IBinder.DeathRecipient mBinderDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
/** 對Binder 斷開 重練回調*/
Log.d("RedRose", "binder died");
iBookManager.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
iBookManager = null;
connectBinderService();
}
};
/**
* 其實這樣寫是有問題的,因爲在服務端註冊和取消註冊是運行在Binder線程池,如果遠程調用方法
* 存在耗時,會導致主線程ANR
* 應該放到線程池去執行,這裏只是側重點是數據更新監聽和取消註冊
*/
public void unregisterListener(View view) {
if (iBookManager != null) {
try {
iBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void registerListener(View view) {
if (iBookManager != null) {
try {
iBookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void addBook(View view) {
try {
if (iBookManager != null) {
iBookManager.addBook(new Book());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void getBookList(View view) {
try {
if (iBookManager != null) {
iBookManager.getBookList();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
Book.aidl和IBookManager.aidl
// Book.aidl
package com.redrose.aidldemo;
parcelable Book;
---------------------------------------------------------------
// IBookManager.aidl
package com.redrose.aidldemo;
// Declare any non-default types here with import statements
import com.redrose.aidldemo.Book;
import com.redrose.aidldemo.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
-------------------------------------------------------------------
// IOnNewBookArrivedListener.aidl
package com.redrose.aidldemo;
// Declare any non-default types here with import statements
import com.redrose.aidldemo.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
AidlService(服務端 爲了更好體現進程通信,請在清單文件指定service的進程),可以查下RemoteCallbackList的使用方法
package com.redrose.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class AidlService extends Service {
/**
* Aidl 中使用的List只有ArrayList
* 但是CopyOnWriteArrayList 並不是繼承ArrayList
* 因爲AIDL中支持的是抽象的List,而List只是一個接口
* 因此服務端返回的是CopyOnWriteArrayList,但是在Binder中會按照
* List的規範去訪問數據並最終形成一個新的ArrayList傳遞給客戶端
*/
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//使用CopyOnWriteArrayList 來存放監聽器是無法實現的,進程間通信傳遞的不是同一個對象,最後導致無法找到,而無法正確移除
//需要使用RemoteCallbackList,因爲多次跨進程傳輸客戶端的同一個對象在服務端生成不同的對象,但是他們底層的Binder對象
//是同一個,所以就很明瞭了。 RemoteCallbackList內部同樣也實現了線程同步的功能,可以查看源碼
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBookBinder;
}
private IBinder mBookBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
onNewBookArray(book);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
Log.d("RedRose", " --registerListener--");
mListenerList.register(listener);
final int num = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d("RedRose", " mListenerList size = " + num);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
Log.d("RedRose", " --unregisterListener--");
mListenerList.unregister(listener);
final int num = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d("RedRose", " mListenerList size = " + num);
}
//在Binder被調用 綁定時調用,用作判斷是否具有權限
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//做權限判斷,沒有權限就返回null
return super.onTransact(code, data, reply, flags);
}
};
private void onNewBookArray(Book book) throws RemoteException {
final int num = mListenerList.beginBroadcast();
for (int i = 0; i < num; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
listener.onNewBookArrived(book);
}
}
mListenerList.finishBroadcast();
}
}
AIDL進程通信之數據監聽,監聽和移除總結實例就到這裏了,寫的比較簡潔,菜鳥肯定還需要多看下AIDL方面的知識點。老司機,勿噴。