IPC机制的AIDL进阶,结合观察者模式

来源:互联网 发布:js substring 编辑:程序博客网 时间:2024/05/17 23:10

好久没写博客了,最近确实挺忙的,感情也不顺利,一直萎靡着,就这样吧,进入主题了.

在半年前写过篇AIDL的入门Demo,< IPC机制第二篇,AIDL实现> 这边的案例实现是,客户端只有去请求服务器,服务器才会返回结果,那假设有这样一个需求:用户不想时不时地去访问,而是希望当服务器有最新咨询的时候能够主动推送到客户端上来,这就是一种典型的观察者模式了,当然里面还有一些问题需要注意的我们等下来总结.

1 . IOnNewBookArrivedListener.aidl

// IOnNewBookArrivedListener.aidlpackage com.tom.aidldemo;// Declare any non-default types here with import statementsimport com.tom.aidldemo.Book;interface IOnNewBookArrivedListener {    void onNewBookArrived(in Book book);}

2 . IBookManager.aidl

// IBookManager.aidlpackage com.tom.aidldemo;// Declare any non-default types here with import statementsimport com.tom.aidldemo.Book;import com.tom.aidldemo.IOnNewBookArrivedListener;interface IBookManager {    List<Book> getBookList();    void addBook(in Book book);    void registerListener(IOnNewBookArrivedListener listener);    void unregisterListener(IOnNewBookArrivedListener listener);}

3 . Book.java

public class Book implements Parcelable {    public int bookId;    public String bookName;    public Book(int bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(this.bookId);        dest.writeString(this.bookName);    }    public Book() {    }    protected Book(Parcel in) {        this.bookId = in.readInt();        this.bookName = in.readString();    }    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {        @Override        public Book createFromParcel(Parcel source) {            return new Book(source);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };}

4 . Book.aidl

// Book.aidl.aidlpackage com.tom.aidldemo;// Declare any non-default types here with import statementsparcelable Book;

5 . 服务器代码BookManagerService.java

public class BookManagerService extends Service {    private static final String TAG = "BookManagerService";    private CopyOnWriteArrayList<Book> mBookList =  new CopyOnWriteArrayList<Book>();    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);//    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =//            new CopyOnWriteArrayList<IOnNewBookArrivedListener>();    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =            new RemoteCallbackList<IOnNewBookArrivedListener>();    public BookManagerService() {    }    private Binder mBinder = new IBookManager.Stub(){        @Override        public List<Book> getBookList() throws RemoteException {            return mBookList;        }        @Override        public void addBook(Book book) throws RemoteException {            mBookList.add(book);            //这里也要进行一次通知观察者,省略了...        }        @Override        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {            mListenerList.register(listener);        }        @Override        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {            mListenerList.unregister(listener);        }    };    @Override    public void onCreate() {        super.onCreate();        mBookList.add(new Book(1, "Android"));        mBookList.add(new Book(2, "Ios"));        new Thread(new ServiceWorker()).start();    }    @Override    public void onDestroy() {        mIsServiceDestoryed.set(true);        super.onDestroy();    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    private class ServiceWorker implements Runnable {        @Override        public void run() {            while (!mIsServiceDestoryed.get()){                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                int bookId = mBookList.size() + 1;                Book newBook = new Book(bookId, "new book#" + bookId);                try {                    onNewBookArray(newBook);                } catch (RemoteException e) {                    e.printStackTrace();                }            }        }    }    private void onNewBookArray(Book newBook) throws RemoteException {        mBookList.add(newBook);        int N = mListenerList.beginBroadcast();        for (int i = 0; i<N; i++){            IOnNewBookArrivedListener broadcastItem = mListenerList.getBroadcastItem(i);            if(broadcastItem != null) {                broadcastItem.onNewBookArrived(newBook);            }        }        mListenerList.finishBroadcast();    }}

6 . 客户端代码MainActivity.java

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;    private IBookManager mRemoteBookManager;    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){                case MESSAGE_NEW_BOOK_ARRIVED:                    Log.d(TAG, "收到新书: " + msg.obj);                    break;                default:                    super.handleMessage(msg);                    break;            }        }    };    private ServiceConnection mConn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IBookManager bookManager = IBookManager.Stub.asInterface(service);            try {                mRemoteBookManager = bookManager;                List<Book> bookList = bookManager.getBookList();                Log.i(TAG, "query book list, list type: " +                        bookList.getClass().getCanonicalName());                Log.i(TAG, "query book list: " + bookList.toString());                Book newBook = new Book(3, "数学之美");                bookManager.addBook(newBook);                Log.i(TAG, "onServiceConnected: " + newBook);                bookList = bookManager.getBookList();                Log.i(TAG, "query book list, list type: " +                        bookList.getClass().getCanonicalName());                Log.i(TAG, "query book list: " + bookList.toString());                bookManager.registerListener(mOnNewBookArrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            mRemoteBookManager = null;            Intent intent = new Intent(MainActivity.this, BookManagerService.class);            bindService(intent, mConn, Context.BIND_AUTO_CREATE);        }    };    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {        @Override        public void onNewBookArrived(Book book) throws RemoteException {            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent(this, BookManagerService.class);        bindService(intent, mConn, Context.BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        if(mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()){            try {                mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        unbindService(mConn);        super.onDestroy();    }}

AIDL难点理解:
1 . AIDL底层是通过Binder实现的,但是Binder会把客户端传来的对象重新转化并生成一个新的对象.虽然我们在观察者模式注册和解注册的时候使用的是同一个对象,但是通过Binder传递到服务器之后会产生两个对象.因为对象是不能够跨进程传输的,实际的传输是Binder的序列化和反序列化的过程,但是两次的序列化操作Binder是同一个.
解决方案使用RemoteCallbackList.

2 . 不管是客户端还是服务器,我们都可以看到每个方法都会抛一个异常RemoteException.为什么?因为进程间的通信,不管是服务端也好还是客户端也好跨进程的方法都是在Binder线程池中的运行的,也就是说是非UI线程.所以切记耗时不能运行在主线程中,不然就会报ANR了.

3 . Binder是会意外死亡的,所以我们要在onServiceDisconnected中进行重连远程服务

4 . 客户端和服务端的权限验证,这个请读者自行百度,主要是在服务端的onBind方法是否返回null来控制.

0 0
原创粉丝点击