上一节 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~

posted on 2016-03-28 23:36  Fishbonell  阅读(375)  评论(0编辑  收藏  举报