上一节 http://www.cnblogs.com/fishbone-lsy/p/5327500.html 主要记录了一个跨进程的图片管理后台,设计了getBookList和addBook两个方法。但不管哪个方法,其实都是客户端对服务端发消息,然后服务端返回消息。没有服务端主动向客户端发消息的情况。所以,在这一节补充一下这种情况。增加一个新书通知的功能。
所谓服务端给客户端发消息,其实质,还是客户端伸给服务端一个接口,服务端在要给客户端发消息时,就调用这个接口,让客户端监听到这一通知。这个接口是跨进程的,因此,我们还是需要用aidl来定义它。
// IOnNewBookArrivedListener.aidl package com.dream.fishbonelsy.aidldemo.aidl; // Declare any non-default types here with import statements import com.dream.fishbonelsy.aidldemo.aidl.Book; interface IOnNewBookArrivedListener { void onNewBookArrived(in Book newBook); }
然后,和上一节的顺序一样,先来写服务端它的实现。我们一个服务端可能同时为多个客户端服务,因此,服务端中可能保存了若干个客户端的接口。客户端决写了自己要不要将接口放入服务端,这其实是一个典型的观察者模式。
因此,我们要在服务中,维护一个存放客户端接口的队列,并提供加入和移出这个队列的方法(即注册与注销)
// BookManagerService.java public class BookManagerService extends Service { AtomicBoolean mIsServiceRunning = new AtomicBoolean(true); CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>(); RemoteCallbackList<IOnNewBookArrivedListener> mNewBookListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>(); private final IBookManager.Stub mBinder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { synchronized (mBookList) { return mBookList; } } @Override public void addBook(Book book) throws RemoteException { synchronized (mBookList) { if (!mBookList.contains(book)) { mBookList.add(book); onNewBookArrived(book); } } } @Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException { mNewBookListenerList.register(listener); } @Override public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException { mNewBookListenerList.unregister(listener); } }; private void onNewBookArrived(Book book) throws RemoteException { int num = mNewBookListenerList.beginBroadcast(); for( int i=0; i < num ; i++){ if (mNewBookListenerList.getBroadcastItem(i) != null){ mNewBookListenerList.getBroadcastItem(i).onNewBookArrived(book); } } mNewBookListenerList.finishBroadcast(); } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onDestroy() { mIsServiceRunning.set(false); super.onDestroy(); } }
我们用一个RemoteCallbackList来存放来自其他进程的接口,它提供了register和unregister方法,我们只需要专注于给服务端发通知即可。
private void onNewBookArrived(Book book) throws RemoteException { int num = mNewBookListenerList.beginBroadcast(); for( int i=0; i < num ; i++){ if (mNewBookListenerList.getBroadcastItem(i) != null){ mNewBookListenerList.getBroadcastItem(i).onNewBookArrived(book); } } mNewBookListenerList.finishBroadcast(); }
RemoteCallbackList为了线程安全,一旦开始发送通知,就会把所有回调都上锁,因此,设计出开始发送和结束发送这样的结构。
服务端的代码完成之后,客户端的依旧是比较容易。
IOnNewBookArrivedListener onNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { Log.d("tag", "get the notify a new book has been added called ===" + newBook.bookName); } }; mService.registerListener(onNewBookArrivedListener);
定义接口,然后注册即可。
值得注意的是,当我们把接口放在Activity中时,我们需要及时的注销接口,否则会导致内存泄露。
@Override protected void onDestroy() { try { mService.unregisterListener(onNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } unbindService(connection); super.onDestroy(); }
通过以上的代码,一个基本的跨进程通信过程算是完成。下一节会介绍一些一多对通信时,遇到的坑。
Done~