天天看点

IPC机制---04 Android中的IPC通讯方式(C)

  • 上一篇中,在服务端的BookService中使用的是ArrayList,这是一个线程不安全的集合,因为AIDL的方法是在Binder的线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情况,所以我们要在AIDL方法中处理线程同步,下面使用一个新的集合类来代替它--CopyOnWriteArrayList
  • CopyOnWriteArrayList
    • 支持并发读/写
    • 能自动进行线程同步
    • 之前提到,AIDL中支持的List只有ArrayList,而我们用其对ArrayList进行替换,但CopyOnWriteArrayList不是继承自ArrayList。
    • 虽然服务端返回的是一个CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端,因此使用CopyOnWriteArrayList是完成可以的,同样的还有ConcurrentHashMap。
  • 下面对该demo进行扩展,使用观察者模式,这样一种需求,用户不需要每次都去进行新书的获取,而是有新书的时候,主动去提醒进行订阅的用户,同样,用户也可以取消订阅。
    • 首先,提供一个AIDL接口,每个用户都需要实现这个接口并向图书馆申请新书的提醒功能,之所以选择AIDL接口是因为AIDL中无法使用普通接口。
  • 服务端
    • 定义IOnNewBookArrivedListener.aidl,提供onNewBookArrived方法,如下:
    // IONewBookArrivedListener.aidl
    package com.happy.ipc.server;
    import com.happy.ipc.server.domain.Book;
    // Declare any non-default types here with import statements
    
    interface IONewBookArrivedListener {
    
       void onNewBookArrived(in Book book);
    
    }
               
    • 在IBookManager.aidl文件中添加注册和取消注册方法,如下:
    // IBookManager.aidl
    package com.happy.ipc.server;
    import com.happy.ipc.server.domain.Book;
    import com.happy.ipc.server.IONewBookArrivedListener;
    // Declare any non-default types here with import statements
    
    interface IBookManager {
    
       int getBookCount();
    
       List<Book> addBook(in Book book);
    
       void registerNewBookArrivedListener(IONewBookArrivedListener listener);
    
       void unRegisterNewBookArrivedListener(IONewBookArrivedListener listener);
    
    }
               
    • 在BookService中实现这两个方法,如下:
    @Override
            public void registerNewBookArrivedListener(IONewBookArrivedListener listener) throws RemoteException {
                if (listenerList.contains(listener)) {
                    Log.i(TAG, "listener already existe");
                } else {
                    listenerList.add(listener);
                    Log.i(TAG, "register listener succsss");
                }
            }
    
            @Override
            public void unRegisterNewBookArrivedListener(IONewBookArrivedListener listener) throws RemoteException {
                if (listenerList.contains(listener)) {
                    listenerList.remove(listener);
                    Log.i(TAG, "unRegister listener success");
                } else {
                    Log.i(TAG, "not found listener");
                }
            }
               
    • 同时在BookService中开启一个线程,每隔五秒添加一本书,如下
    private class WorkService implements Runnable {
            @Override
            public void run() {
                while (!mIsServiceDestoryed.get()) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Book book = new Book(bookList.size() + 1, "new book" + bookList.size());
                    try {
                        onNewBookArrived(book);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        private void onNewBookArrived(Book book) throws RemoteException {
            bookList.add(book);
            for (IONewBookArrivedListener listener : listenerList) {
                listener.onNewBookArrived(book);
            }
        }
               
  • 客户端
    • 首先将刚才定义的aidl文件拷贝到客户端相同目录下
    • 在绑定服务成功后,进行注册监听,如下
    private IONewBookArrivedListener listener = new IONewBookArrivedListener.Stub() {
            @Override
            public void onNewBookArrived(Book book) throws RemoteException {
                Log.i(TAG, "onNewBookArrived : " + book.toString());
            }
        };
        private ServiceConnection conn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mBookManager = IBookManager.Stub.asInterface(service);
                try {
                    mBookManager.registerNewBookArrivedListener(listener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mBookManager = null;
            }
        };
               
    • 在onDestory方法中进行解绑监听和服务,如下:
    @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mBookManager != null && mBookManager.asBinder().isBinderAlive()) {
                try {
                    mBookManager.unRegisterNewBookArrivedListener(listener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            unbindService(conn);
        }
               
  • 通过log日志查看,发现达到预期,每隔五秒添加一本新书并通知客户端,并且可以进行监听的动态注册和解绑,如下:
    IPC机制---04 Android中的IPC通讯方式(C)
    IPC机制---04 Android中的IPC通讯方式(C)
  • 但是,在activity关闭,进行注册监听解绑的时候,却发现log日志为 这是为什么呢,下篇进行讲述吧。
    IPC机制---04 Android中的IPC通讯方式(C)
  • 另外,服务端回调客户端的onBookArrived方法,是在客户端的Binder线程池中进行的,这里应该通过Handler将其发送到客户端的主线程中去执行,修改后代码如下:
    private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MESSAGE_BOOK_ARRIVED:
                        Log.i(TAG, "onNewBookArrived : " + msg.obj.toString());
                        break;
                }
            }
        };
    
        private IONewBookArrivedListener listener = new IONewBookArrivedListener.Stub() {
            @Override
            public void onNewBookArrived(Book book) throws RemoteException {
                mHandler.obtainMessage(MESSAGE_BOOK_ARRIVED, book).sendToTarget();
            }
        };