進程間通信系列
AIDL的入門使用(一)
AIDL的入門使用(二)
AIDL的入門使用(三)
Messenger的入門使用
序言:
在Android 的進程間通信 Binder——AIDL的入門使用(一)中我們可以通過AIDL調(diào)用服務端的方法進行操作矛洞,那可不可以反過來呢挂谍,服務端調(diào)用客戶端的方法夫植,場景:圖書館有新書時自動通知所有訂閱的讀者;這里就可以使用觀察者模式尿赚,客戶端在服務端注冊一個接口,當服務端有新書,自動調(diào)用客戶端注冊的接口。這種方式也可以用于消息推送的通知其他進程(猜測)祝谚。
對服務端進行的改造:(過程參考《Android開發(fā)藝術(shù)探索》)
1、定義一個當有新書到來時的通知接口酣衷,由于需在客戶端回調(diào)使用到了跨進程交惯,所以需要定義在AIDL文件中。
// IOnNewBookArrivedListener.aidl
package com.ljp.aidl_server.aidl;
// Declare any non-default types here with import statements
import com.ljp.aidl_server.aidl.Book;//雖然在同一個包中穿仪,但還是要進行導包操作席爽,否則會報錯。
interface IOnNewBookArrivedListener {//當有新書的時候的通知接口啊片。
void onNewBookArrived(in Book newBook);
}
2只锻、在IMyAidlInterface.aidl文件中增加注冊和解除注冊的方法。
// IMyAidlInterface.aidl
package com.ljp.aidl_server.aidl;
// Declare any non-default types here with import statements
import com.ljp.aidl_server.aidl.Book;//雖然在同一個包中紫谷,但還是要進行導包操作齐饮,否則會報錯。
import com.ljp.aidl_server.aidl.IOnNewBookArrivedListener;
interface IMyAidlInterface {
........
void registerListener(in IOnNewBookArrivedListener listener);// 注冊監(jiān)聽
void unregisterListener(in IOnNewBookArrivedListener listener);//解除注冊
}
3笤昨、在AidlSerVerService中的IMyAidlInterface.Stub實現(xiàn)registerListener和unregisterListener方法祖驱,在onCreat方法中開啟一個線程,每隔5秒就加入一本新書并通知已注冊的客戶端瞒窒。
package com.ljp.aidl_server.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class AidlSerVerService extends Service {
private static final String TAG = "AidlSerVerService";
private volatile boolean mIsServiceDestoryed = false;//Service是否銷毀
// 用于保存跨進程接口回調(diào)的對象捺僻,已自動實現(xiàn)了線程同步
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
//CopyOnWriteArrayList支持并發(fā)讀寫并自動進行線程同步 ,
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private String tag = "empty";
private int num = -1;
private IMyAidlInterface.Stub stub_binder = new IMyAidlInterface.Stub() {//IMyAidlInterface.Stub為Binder的子類
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void addBook(Book book) throws RemoteException {
addBookNotify(book);
}
@Override
public List<Book> getBooks() throws RemoteException {
return mBookList;//這里雖然返回的是CopyOnWriteArrayList,但底層會按照ArrayList進行讀取然后返回給客戶端
}
@Override
public void registerListener(com.ljp.aidl_server.aidl.IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
Log.e(TAG, "registerListener: 已經(jīng)注冊Size=" + mListenerList.getRegisteredCallbackCount());
}
@Override
public void unregisterListener(com.ljp.aidl_server.aidl.IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
Log.e(TAG, "unregisterListener: 現(xiàn)注冊的Size=" + mListenerList.getRegisteredCallbackCount());
}
@Override
public void setTag(String tag) throws RemoteException {
synchronized (this) {//進行同步操作匕坯,有可能并發(fā)
if (!TextUtils.isEmpty(tag)) {
AidlSerVerService.this.tag = tag;
}
}
}
@Override
public String getTag() throws RemoteException {
return tag;
}
@Override
public void setNum(int num) throws RemoteException {
synchronized (this) {
AidlSerVerService.this.num = num;
}
}
@Override
public int getNum() throws RemoteException {
return num;
}
};
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return stub_binder;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(100, "Android開發(fā)", 20));
mBookList.add(new Book(101, "Java開發(fā)", 22));
new Thread(new Worker()).start(); //開啟一個線程束昵,每5秒添加一本新書并通知到已注冊的全部客戶端
Log.e("aidl_server_Service", "onCreate: ");
}
@Override
public void onDestroy() {
mIsServiceDestoryed=true;
super.onDestroy();
}
/**
* 添加書籍并通知客戶端
* @param newBook 添加的新書
* @throws RemoteException
*/
private void addBookNotify(Book newBook) throws RemoteException {
mBookList.add(newBook);
int count = mListenerList.beginBroadcast();//必須與 finishBroadcast()方法配對使用
Log.e(TAG, "addBookNotify: 通知所有的監(jiān)聽器,size= " + count);
for (int i = 0; i < count; i++) {
IOnNewBookArrivedListener item = mListenerList.getBroadcastItem(i);
if (item != null) {
item.onNewBookArrived(newBook);//調(diào)用客戶端的方法醒颖,通知客戶端妻怎,由于調(diào)用了客戶端的方法為耗時操作建議放在子線程中
}
}
mListenerList.finishBroadcast();//必須與 beginBroadcast()方法配對使用
}
private class Worker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed) {
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new book #" + bookId, bookId * 1.5);
try {
addBookNotify(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
上面使用到了CopyOnWriteArrayList和RemoteCallbackList,這里的監(jiān)聽器管理不能使用List泞歉,否則在最后解除注冊不會成功(因為服務端收到的listener每次都是新創(chuàng)建的對象)逼侦,因此采用RemoteCallbackList管理。
CopyOnWriteArrayList:支持并發(fā)讀寫腰耙,自動進行線程同步榛丢,使用和ArrayList相同,實現(xiàn)了List接口挺庞,但和ArrayList沒有任何關(guān)系晰赞。類似的還有ConcurrentHashMap
RemoteCallbackList:系統(tǒng)專門提供用于刪除跨進程listener的接口,使用了泛型支持管理任意的AIDL接口选侨,其內(nèi)部采用ArrayMap<IBinder, Callback>鍵值對的方式存儲掖鱼,當客戶端進程終止后它能自動移除客戶端已注冊的listener,援制,內(nèi)部已自動實現(xiàn)了線程同步戏挡,beginBroadcast()必須與 finishBroadcast()方法配對使用
對客戶端的改造:
將服務端的AIDL包復制到客戶端的對應位置下,創(chuàng)建一個監(jiān)聽器對象在綁定服務端成功后注冊晨仑,在onDestory方法中解除注冊褐墅。
@Override
protected void onDestroy() {
UnbindAidlService(null);
super.onDestroy();
}
private IMyAidlInterface mService_face;
private static final String TAG = "Main_Client";
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: ");
mService_face = IMyAidlInterface.Stub.asInterface(service);
try {
service.linkToDeath(mDeathRecipient, 0);//客戶端遠程綁定服務成功后,給binder設(shè)置死亡代理洪己,當服務端的binder對象死亡時系統(tǒng)回調(diào)mDeathRecipient.binderDied()方法妥凳,service.isBinderAlive();//可以判斷服務端的Binder是否死亡
mService_face.registerListener(mIOnNewBookArrivedListener);//2、綁定成功后注冊通知的監(jiān)聽器答捕。
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: ");
}
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
//當Binder死亡時逝钥,系統(tǒng)會回調(diào)該方法,在此移除之前綁定的Binder代理并重新綁定遠程服務
if (mService_face == null) return;
mService_face.asBinder().unlinkToDeath(mDeathRecipient, 0);
mService_face = null;
Log.e(TAG, "binderDied: Binder死亡時,移除之前綁定的Binder代理并重新綁定遠程服務");
bindAidlService(null);
}
};
//1拱镐、創(chuàng)建一個監(jiān)聽器用于服務端添加新書時的通知晌缘。
private IOnNewBookArrivedListener mIOnNewBookArrivedListener=new IOnNewBookArrivedListener.Stub(){
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
Log.e(TAG, "onNewBookArrived: 收到了服務端更新的通知,newbook="+newBook );
}
};
public void bindAidlService(View view) {
Intent intent_service = new Intent();
intent_service.setPackage("com.ljp.aidl_server"); //設(shè)置需要綁定的服務端的包名痢站,不是服務端Service的包名
intent_service.setAction("server.aidl.service.action");//設(shè)置你所需調(diào)用服務的意圖
boolean successful = bindService(intent_service, mConnection, BIND_AUTO_CREATE);
Log.e(TAG, "bindAidlService: successful=" + successful);
}
public void UnbindAidlService(View view) {
//3、客戶端銷毀時解除注冊在服務端的監(jiān)聽器
if (mService_face!=null&&mService_face.asBinder().isBinderAlive()){
try {
mService_face.unregisterListener(mIOnNewBookArrivedListener);//解除在服務端的注冊
} catch (RemoteException e) {
e.printStackTrace();
}
}
if (mConnection != null) {
unbindService(mConnection);
Log.e(TAG, "UnbindAidlService: ");
}
}
測試結(jié)果:
12-01 10:59:12.689 4143-4143/com.ljp.aidl_client E/aidlLog: bindAidlService: successful=true
12-01 10:59:12.719 4143-4143/com.ljp.aidl_client E/aidlLog: onServiceConnected:
12-01 10:59:12.719 4352-4364/com.ljp.aidl_server:remote E/aidlLog: registerListener: 已經(jīng)注冊Size=1
12-01 10:59:16.249 4352-4363/com.ljp.aidl_server:remote E/aidlLog: addBookNotify: 通知所有的監(jiān)聽器选酗,size= 1
12-01 10:59:16.249 4143-4143/com.ljp.aidl_client E/aidlLog: onNewBookArrived: 收到了服務端更新的通知阵难,newbook=Book{id=0, name='book0', price=30.5}
12-01 10:59:16.249 4143-4143/com.ljp.aidl_client E/aidlLog: addBook_AidlService:
12-01 10:59:17.709 4352-5093/com.ljp.aidl_server:remote E/aidlLog: addBookNotify: 通知所有的監(jiān)聽器,size= 1
12-01 10:59:17.709 4143-4158/com.ljp.aidl_client E/aidlLog: onNewBookArrived: 收到了服務端更新的通知芒填,newbook=Book{id=4, name='new book #4', price=6.0}
12-01 10:59:22.699 4352-5093/com.ljp.aidl_server:remote E/aidlLog: addBookNotify: 通知所有的監(jiān)聽器呜叫,size= 1
12-01 10:59:22.709 4143-4157/com.ljp.aidl_client E/aidlLog: onNewBookArrived: 收到了服務端更新的通知空繁,newbook=Book{id=5, name='new book #5', price=7.5}
12-01 10:59:24.879 4352-4364/com.ljp.aidl_server:remote E/aidlLog: unregisterListener: 現(xiàn)注冊的Size=0
12-01 10:59:24.879 4143-4143/com.ljp.aidl_client E/aidlLog: UnbindAidlService:
12-01 10:59:27.709 4352-5093/com.ljp.aidl_server:remote E/aidlLog: addBookNotify: 通知所有的監(jiān)聽器,size= 0
我的CSDN博客地址:http://blog.csdn.net/wo_ha/article/details/78684695