- AIDL的基本使用過(guò)程可以看這篇文章http://www.reibang.com/p/2683e27efe9a
業(yè)務(wù)場(chǎng)景:
現(xiàn)在要實(shí)現(xiàn)每新增一個(gè)員工,就通知相應(yīng)的部門人員
1蔚鸥、提供一個(gè)AIDL接口庞瘸,由于AIDL中無(wú)法使用普通接口捧弃,所以提供一個(gè)AIDL接口
// IOnNewPersonArrivedListener.aidl
package com.wuc.aidltest;
// Declare any non-default types here with import statements
import com.wuc.aidltest.Person;
// 當(dāng)服務(wù)端有新人加入時(shí),就通知每一個(gè)已經(jīng)申請(qǐng)?zhí)嵝压δ艿挠脩簦捎贏IDL中無(wú)法使用普通接口违霞,所以提供一個(gè)AIDL接口
interface IOnNewPersonArrivedListener {
void onNewPersonArrived(in Person person);
}
2嘴办、 修改ICalculateInterface代碼,新增注冊(cè)與注銷監(jiān)聽(tīng)器代碼
// ICalculateInterface.aidl
package com.wuc.aidltest;
// Declare any non-default types here with import statements
import com.wuc.aidltest.Person;
import com.wuc.aidltest.IOnNewPersonArrivedListener;
interface ICalculateInterface {
//計(jì)算兩個(gè)數(shù)的和
int addNum(int num1,int num2);
//除了基本數(shù)據(jù)類型买鸽,其他類型的參數(shù)都需要標(biāo)上方向類型:in(輸入), out(輸出), inout(輸入輸出)
List<Person> addPerson(in Person person);
void registerListener(IOnNewPersonArrivedListener listener);
void unregisterListener(IOnNewPersonArrivedListener listener);
}
IRemoteService代碼
package com.wuc.aidltest;
public class IRemoteService extends Service {
private static final String TAG = "IRemoteService";
//存儲(chǔ)注冊(cè)監(jiān)聽(tīng)客戶端集合
private final CopyOnWriteArrayList <IOnNewPersonArrivedListener> mListenerList = new CopyOnWriteArrayList <>();
/**
* CopyOnWriteArrayList支持并發(fā)讀寫涧郊,AIDL方法是在服務(wù)端的Binder線程池中執(zhí)行的,因此當(dāng)多個(gè)客戶端同時(shí)連接的時(shí)候眼五,
* 會(huì)存在多個(gè)線程同時(shí)訪問(wèn)的情形妆艘,所以我們要在AIDL方法中處理線程同步,這里使用CopyOnWriteArrayList來(lái)進(jìn)行自動(dòng)的線程同步
* <p>
* 因?yàn)锳IDL中所支持的是抽象的List看幼,二List只是一個(gè)接口双仍,因此雖然服務(wù)端返回的是CopyOnWriteArrayList,但是在Binder中
* 會(huì)按照List的規(guī)范去訪問(wèn)數(shù)據(jù)并最終形成一個(gè)新的ArrayList傳遞給客戶端桌吃,所以采用CopyOnWriteArrayList是可以的朱沃,類似的
* 還有ConcurrentHashMap
*/
private CopyOnWriteArrayList<Person> mPersonList;
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
private IBinder mIBinder = new ICalculateInterface.Stub() {
@Override
public int addNum(int num1, int num2) throws RemoteException {
return num1 + num2;
}
@Override
public List<Person> addPerson(Person person) throws RemoteException {
mPersonList.add(person);
return mPersonList;
}
@SuppressLint("NewApi")
@Override
public void registerListener(IOnNewPersonArrivedListener listener) throws RemoteException {
if (!mListenerList.contains(listener)) {
mListenerList.add(listener);//添加監(jiān)聽(tīng)
} else {
Log.d(TAG, "already exists.");
}
Log.d(TAG, "registerListener,size:" + mListenerList.size());
}
@SuppressLint("NewApi")
@Override
public void unregisterListener(IOnNewPersonArrivedListener listener) throws RemoteException {
if (mListenerList.contains(listener)) {
mListenerList.remove(listener);//移除監(jiān)聽(tīng)
Log.d(TAG, "unregister listener succeed.");
} else {
Log.d(TAG, "not found,can not unregister.");
}
Log.d(TAG, "unregisterListener茅诱,current size:" + mListenerList.size());
}
};
@Override
public void onCreate() {
super.onCreate();
new Thread(new ServiceWorker()).start();
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
mPersonList = new CopyOnWriteArrayList<>();
return mIBinder;
}
private void onNewPersonArrived(Person person) throws RemoteException {
mPersonList.add(person);
Log.d(TAG, "onNewPersonArrived, notify listener:" + mListenerList.size());
for (int i = 0; i < mListenerList.size(); i++) {
IOnNewPersonArrivedListener listener = mListenerList.get(i);
Log.d(TAG, "onNewPersonArrived, notify listener:" + listener);
listener.onNewPersonArrived(person);
}
}
/**
* 每個(gè)5秒增加一個(gè)新人逗物,并通知所有感興趣的員工
*/
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Person person = new Person("new person name:" + 2, 22);
try {
onNewPersonArrived(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
MainActivity代碼
package com.wuc.aidlclient;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int MESSAGE_NEW_PERSON_ARRIVED = 1;
private AppCompatEditText mEdt_num1;
private AppCompatEditText mEdt_num2;
private AppCompatButton mBtn_calculate;
private AppCompatTextView mTxt_result;
private ICalculateInterface mICalculateInterface;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mICalculateInterface == null) {
return;
}
//移除之前綁定的代理并重新綁定遠(yuǎn)程服務(wù)
mICalculateInterface.asBinder().unlinkToDeath(mDeathRecipient, 0);
mICalculateInterface = null;
bindService();
}
};
private ClientHandler mHandler = new ClientHandler();
/**
* 當(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)求,為了避免阻塞UI 線程出現(xiàn)ANR
* 由于服務(wù)端的Binder 方法運(yùn)行在 Binder 的線程池中摆寄,所以 Binder 方法不管是否耗時(shí)都應(yīng)該采用同步的方式去實(shí)現(xiàn)失暴,
* 因?yàn)樗呀?jīng)運(yùn)行在一個(gè)線程中了
*/
private IOnNewPersonArrivedListener mOnNewPersonArrivedListener = new IOnNewPersonArrivedListener.Stub() {
@Override
public void onNewPersonArrived(Person person) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_PERSON_ARRIVED, person).sendToTarget();
}
};
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//判斷Binder是否死忙
//boolean binderAlive = service.isBinderAlive();
//用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換為客戶端需要的AIDL接口類型的對(duì)象
mICalculateInterface = ICalculateInterface.Stub.asInterface(service);
try {
mICalculateInterface.registerListener(mOnNewPersonArrivedListener);
//給binder設(shè)置死忙代理,當(dāng)Binder死忙時(shí)就可以收到通知
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//連接斷開(kāi)微饥,釋放AIDL Binder對(duì)象
mICalculateInterface = null;
Log.d(TAG, "binder died");
}
};
@SuppressLint("CutPasteId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEdt_num1 = findViewById(R.id.edt_num1);
mEdt_num2 = findViewById(R.id.edt_num2);
mTxt_result = findViewById(R.id.txt_result);
mBtn_calculate = findViewById(R.id.btn_calculate);
mBtn_calculate.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n")
@Override
public void onClick(View v) {
int num1 = Integer.parseInt(mEdt_num1.getText().toString());
int num2 = Integer.parseInt(mEdt_num2.getText().toString());
try {
int num = mICalculateInterface.addNum(num1, num2);
mTxt_result.setText("結(jié)果:" + num);
} catch (RemoteException e) {
e.printStackTrace();
mTxt_result.setText("計(jì)算錯(cuò)誤");
}
try {
List<Person> personList = mICalculateInterface.addPerson(new Person("wuc", 22));
Log.d("aidl", personList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
bindService();
}
private void bindService() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.wuc.aidltest",
"com.wuc.aidltest.IRemoteService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
//如果連接持續(xù)逗扒,并且Binder未死亡
if (mICalculateInterface != null && mICalculateInterface.asBinder().isBinderAlive()) {
try {
Log.d(TAG, "unregister listener : " + mOnNewPersonArrivedListener);
mICalculateInterface.unregisterListener(mOnNewPersonArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(conn);
super.onDestroy();
}
/**
* 防止Handler泄漏
*/
private static class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_PERSON_ARRIVED:
Log.d(TAG, "receive new person : " + msg.obj);
break;
default:
super.handleMessage(msg);
break;
}
}
}
}
Log日志
從日志中可以看出,在解注冊(cè)的過(guò)程中欠橘,服務(wù)端找不到之前注冊(cè)的listener矩肩;
原因
Binder 會(huì)把客戶端傳遞過(guò)來(lái)的對(duì)象重新轉(zhuǎn)化并生成一個(gè)新的對(duì)象,雖然我們?cè)谧?cè)和解注冊(cè)過(guò)程中使用的是同一個(gè)客戶端肃续,但是通過(guò) Binder 傳遞到服務(wù)端后黍檩,卻會(huì)產(chǎn)生兩個(gè)全新的對(duì)象。而對(duì)象是不能跨進(jìn)程傳輸?shù)氖济瑢?duì)象的跨進(jìn)程傳輸本質(zhì)上都是反序列化的過(guò)程刽酱,這就是為什么 AIDL 中的自定義對(duì)象都必須要實(shí)現(xiàn) Parcelable 接口的原因
解決辦法
用RemoteCallbackList,RemoteCallbackList 是系統(tǒng)專門提供的用于刪除跨進(jìn)程 listener 的類瞧捌,RemoteCallbackList 是一個(gè)泛型棵里,支持管理任意的 AIDL 接口,從它的聲明可以看出,因?yàn)樗械?AIDL 接口都繼承自 IInteface 接口
修改后的IRemoteService
package com.wuc.aidltest;
public class IRemoteService extends Service {
private static final String TAG = "IRemoteService";
//存儲(chǔ)注冊(cè)監(jiān)聽(tīng)客戶端集合
private final RemoteCallbackList<IOnNewPersonArrivedListener> mListenerList = new RemoteCallbackList<>();
/**
* CopyOnWriteArrayList支持并發(fā)讀寫衍慎,AIDL方法是在服務(wù)端的Binder線程池中執(zhí)行的转唉,因此當(dāng)多個(gè)客戶端同時(shí)連接的時(shí)候,
* 會(huì)存在多個(gè)線程同時(shí)訪問(wèn)的情形稳捆,所以我們要在AIDL方法中處理線程同步赠法,這里使用CopyOnWriteArrayList來(lái)進(jìn)行自動(dòng)的線程同步
* <p>
* 因?yàn)锳IDL中所支持的是抽象的List,二List只是一個(gè)接口乔夯,因此雖然服務(wù)端返回的是CopyOnWriteArrayList砖织,但是在Binder中
* 會(huì)按照List的規(guī)范去訪問(wèn)數(shù)據(jù)并最終形成一個(gè)新的ArrayList傳遞給客戶端,所以采用CopyOnWriteArrayList是可以的末荐,類似的
* 還有ConcurrentHashMap
*/
private CopyOnWriteArrayList<Person> mPersonList;
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
private IBinder mIBinder = new ICalculateInterface.Stub() {
@Override
public int addNum(int num1, int num2) throws RemoteException {
return num1 + num2;
}
@Override
public List<Person> addPerson(Person person) throws RemoteException {
mPersonList.add(person);
return mPersonList;
}
@SuppressLint("NewApi")
@Override
public void registerListener(IOnNewPersonArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
/* if (!mListenerList.contains(listener)) {
mListenerList.add(listener);
} else {
Log.d(TAG, "already exists.");
}*/
Log.d(TAG, "registerListener侧纯,size:" + mListenerList.getRegisteredCallbackCount());
}
@SuppressLint("NewApi")
@Override
public void unregisterListener(IOnNewPersonArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
/* if (mListenerList.contains(listener)) {
mListenerList.remove(listener);
Log.d(TAG, "unregister listener succeed.");
} else {
Log.d(TAG, "not found,can not unregister.");
}*/
Log.d(TAG, "unregisterListener,current size:" + mListenerList.getRegisteredCallbackCount());
}
};
@Override
public void onCreate() {
super.onCreate();
new Thread(new ServiceWorker()).start();
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
mPersonList = new CopyOnWriteArrayList<>();
int check = checkCallingOrSelfPermission("com.wuc.aidlservice.permission.ACCESS_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mIBinder;
}
private void onNewPersonArrived(Person person) throws RemoteException {
mPersonList.add(person);
/*Log.d(TAG, "onNewPersonArrived, notify listener:" + mListenerList.size());
for (int i = 0; i < mListenerList.size(); i++) {
IOnNewPersonArrivedListener listener = mListenerList.get(i);
Log.d(TAG, "onNewPersonArrived, notify listener:" + listener);
listener.onNewPersonArrived(person);
}*/
synchronized (mListenerList) {
int n = mListenerList.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
IOnNewPersonArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
listener.onNewPersonArrived(person);
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
mListenerList.finishBroadcast();
}
}
/**
* 每個(gè)5秒增加一個(gè)新人甲脏,并通知所有感興趣的員工
*/
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Person person = new Person("new person name:" + 2, 22);
try {
onNewPersonArrived(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
注: RemoteCallbackList 并不是一個(gè)List 眶熬,不能像 List 一樣去操作它,遍歷RemoteCallbackList 必須要以下面的方式進(jìn)行块请,其中 beginBroadcast 和 finishBroadcast 必須配套使用娜氏,哪怕我們僅僅是想要獲取 RemoteCallbackList 中的元素個(gè)數(shù)
int n = mListenerList.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
IOnNewPersonArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
listener.onNewPersonArrived(person);
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
mListenerList.finishBroadcast();
注:
客戶端遠(yuǎn)程調(diào)用服務(wù)端的方法,被調(diào)用的方法運(yùn)行在服務(wù)端的 Binder 線程池中墩新,同時(shí)客戶端線程會(huì)被掛起贸弥,此時(shí)如果服務(wù)端方法執(zhí)行比較耗時(shí),則會(huì)導(dǎo)致客戶端線程長(zhǎng)時(shí)間阻塞在這里海渊,如果此時(shí)客戶端線程是 UI 線程绵疲,則會(huì)導(dǎo)致客戶端ANR ,因此如果我們明確知道某個(gè)遠(yuǎn)程方法是耗時(shí)的臣疑,則要避免在客戶端的 UI 線程中去訪問(wèn)遠(yuǎn)程方法盔憨。
由于客戶端的 onServiceConnected 和 onServiceDisconnected 方法運(yùn)行在 UI線程中,故也不可以在他們里面直接調(diào)用服務(wù)端的耗時(shí)方法朝捆。
服務(wù)端方法本身就運(yùn)行在服務(wù)端的Binder 線程池中般渡,故服務(wù)端的方法本身就可以進(jìn)行大量的耗時(shí)操作,此時(shí)切記不要在服務(wù)端開(kāi)線程去進(jìn)行異步任務(wù)芙盘,除非你明確知道自己在干什么,否則不建議這么做
同理脸秽,當(dāng)遠(yuǎn)程服務(wù)端需要調(diào)用客戶端的 listener 中的方法時(shí)儒老,被調(diào)用的方法運(yùn)行在客戶端的 Binder 池中,故我們同樣不可以在服務(wù)端調(diào)用客戶端耗時(shí)方法记餐,比如針對(duì) IRemoteService 的 onNewPersonArrived 方法驮樊,在它內(nèi)部調(diào)用了客戶端的 IOnNewPersonArrivedListener 中的 onNewPersonArrived 方法,如果客戶端的這個(gè) onNewPersonArrived 方法比較耗時(shí)的話,確保 IRemoteService 的onNewPersonArrived 運(yùn)行在非 UI 線程中囚衔,否則將導(dǎo)致服務(wù)端無(wú)法響應(yīng)
同時(shí)由于客戶端的 IOnNewBookArrivedListener 中的 onNewBookArrived 方法運(yùn)行在客戶端的 Binder 池中挖腰,故不能在里面訪問(wèn)UI相關(guān)的內(nèi)容,如要訪問(wèn)练湿,請(qǐng)用Handler 切換到主線程
Binder是可能意外死亡的猴仑,這往往是由于服務(wù)端進(jìn)程意外停止導(dǎo)致的,此時(shí)我們需要重新連接服務(wù)肥哎。
1辽俗、給Binder 設(shè)置DeathRecipient 監(jiān)聽(tīng),當(dāng)Binder 死亡時(shí)篡诽,我們會(huì)收到 binderDied 方法的回調(diào)崖飘,在 binderDied 方法中我們可以重新綁定遠(yuǎn)程服務(wù)
2、在onServiceDisconnected 中重連遠(yuǎn)程服務(wù)
這兩種方法的區(qū)別在于:onServiceDisconnected 在客戶端的 UI 線程中被回調(diào)杈女,而 binderDied 在客戶端的Binder 線程池中被回調(diào)朱浴,即在binderDied 方法中我們不能訪問(wèn) UI
如何在 AIDL 中使用權(quán)限驗(yàn)證功能?
- 在onBind 中進(jìn)行驗(yàn)證达椰,驗(yàn)證不通過(guò)直接返回null 翰蠢,這樣驗(yàn)證失敗的客戶端直接無(wú)法綁定服務(wù),至于驗(yàn)證方式有很多種砰碴,比如使用permission 驗(yàn)證躏筏,使用這種驗(yàn)證方式,我們需要先在 AndroidMenifest 中聲明所需的權(quán)限呈枉,比如:
<!--定義權(quán)限-->
<permission
android:name="com.wuc.aidlservice.permission.ACCESS_SERVICE"
android:protectionLevel="normal"/>
定義權(quán)限后趁尼,在IRemoteService的onBinder中做權(quán)限驗(yàn)證
@Nullable
@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.wuc.aidlservice.permission.ACCESS_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mIBinder;
}
一個(gè)應(yīng)用來(lái)綁定我們的服務(wù)時(shí),會(huì)驗(yàn)證這個(gè)應(yīng)用的權(quán)限猖辫,如果他沒(méi)有使用這個(gè)權(quán)限酥泞,則onBind 方法就會(huì)直接返回 null,最終這個(gè)應(yīng)用無(wú)法綁定到我們的服務(wù)啃憎,這樣就達(dá)到了權(quán)限驗(yàn)證的效果芝囤,這種方法同樣適用于 Messenger中。
如果我們自己內(nèi)部的應(yīng)用想要綁定到我們的服務(wù)中辛萍,只需在它的 AndroidMenifest 文件中采用如下方式使用 permission 即可
<uses-permission android:name="com.wuc.aidlservice.permission.ACCESS_SERVICE"/>