好久没写博客了,最近确实挺忙的,感情也不顺利,一直萎靡着,就这样吧,进入主题了.
在半年前写过篇AIDL的入门Demo,< IPC机制第二篇,AIDL实现> 这边的案例实现是,客户端只有去请求服务器,服务器才会返回结果,那假设有这样一个需求:用户不想时不时地去访问,而是希望当服务器有最新咨询的时候能够主动推送到客户端上来,这就是一种典型的观察者模式了,当然里面还有一些问题需要注意的我们等下来总结.
1 . IOnNewBookArrivedListener.aidl
// IOnNewBookArrivedListener.aidl
package com.tom.aidldemo;
// Declare any non-default types here with import statements
import com.tom.aidldemo.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
2 . IBookManager.aidl
// IBookManager.aidl
package com.tom.aidldemo;
// Declare any non-default types here with import statements
import 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.aidl
package com.tom.aidldemo;
// Declare any non-default types here with import statements
parcelable 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来控制.