在以上基础上调用添加书本接口,如下方法:先查看一下书籍列表,然后添加书籍,然后再请求书籍列表。
修改的代码如下:
public void onServiceConnected(ComponentName name, IBinder service) {
// 通过Binder来传递bookManager
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
Log.i(TAG, "query book list:" + list.toString());
Book newBook = new Book(3, "android_3");
bookManager.addBook(newBook);
Log.i(TAG, "add book:" + newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query book list:" + newList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
用AIDL实现观察者模式:
1、创建一个添加新书的接口,并在IBookManager中添加两个连个新的方法
添加一个新书的aidl接口,用来监听新书的变化
package com.wscq.myaidl.aidl;
import com.wscq.myaidl.aidl.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
然后在IBookManager方法中添加两个方法,用来注册和解除注册监听器,最后两个方法是新添加的方法。
package com.wscq.myaidl.aidl;
import com.wscq.myaidl.aidl.Book;
import com.wscq.myaidl.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
2、在BookManagerService中开启一个线程
开启一个线程监听书籍的变化,如果有新书,通过AIDL通知客户端的监听器,输出到Logcat
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 {
// 通过AIDL发送消息到客户端
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
这个是发送消息的方法
/** 当有新书添加的时候,服务端通过aidl调用客户端,然后显示添加新书成功,如果此处是耗时操作,需要通过handler来执行 */
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
if (l != null) {
try {
l.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
// 这个要和beginBroadcast配对使用,就算仅仅是获取个数
mListenerList.finishBroadcast();
}
3、修改客户端代码
当服务连接时候注册一个IOnNewBookArrivedListener到远程客户端。
private ServiceConnection mConnection = new ServiceConnection() {
// 服务连接后
@Override
/**
* 服务连接以后执行此方法
*/
public void onServiceConnected(ComponentName name, IBinder service) {
// 通过aidl中的方法,把Binder转化为AIDL本身
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBookManager = null;
Log.i(TAG, "binder died");
}
};
当服务端有新书添加时候,会回调客户端的IOnNewBookArrivedListener对象中的onNewBookArrived方法,由于这个模拟的是耗时操作,所以需要通过handler来发消息执行
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
对应得handler方法:
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "receive new book:" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
4、RemoteCallbackList使用
RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。RemoteCallbackList是一个范型,支持管理任意的AIDL接口。
声明以及注册和解除注册的方法如下:
/** 使用这个list的原因是他可以进行自动的线程同步,类似的还有ConcurrentHashMap */
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
/** 下列方法本身就是运行在Binder线程池中,所以可以执行一些耗时操作 */
private Binder mBinder = new IBookManager.Stub() {
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
};
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
};
};
注意,在循环遍历时候用到了两个方法beginBroadcast和finishBroadcast,这两个方法必须配对使用,计时仅仅是查看一个数量也需要这么用。
5、权限验证方式:
例如我们在服务端为此应用声明了这个权限:
<permission
android:name="com.wscq.aidl.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal" >
</permission>
那么我们可以在服务端的onBind方法中添加如下验证:
public IBinder onBind(Intent intent) {
// TODO:可以在此处对intent进行验证,验证不通过就直接返回null,这样就会这客户端无法直接绑定服务
int check = checkCallingOrSelfPermission("com.wscq.aidl.ACCESS_BOOK_SERVICE");
// 权限验证不通过返回null
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
也可以在onTransaction方法中进行验证:
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// TODO:也可以在此处验证调用端是否有权限调用service
int check = checkCallingOrSelfPermission("com.wscq.aidl.ACCESS_BOOK_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (!packageName.startsWith("com.wscq")) {// 用来验证包名,防止被胡乱调用
return false;
}
return super.onTransact(code, data, reply, flags);
};