【Android开发艺术】读懂AIDL系列文章(二)
本想写一篇文章读懂AIDL的,没想到写了两篇。本文基于上一篇文章: 【Android开发艺术】读懂AIDL系列文章(一)。
一、回顾:
上一篇文章实现:客户端app从服务端app获取数据、添加数据。
二、目的:
本文主要实现:服务端数据更新的时候(本例中即:有新书被添加的时候)告知客户端。类似通知功能。
2.1、如何实现:简而言之,如下。
1、服务端新建线程:每5秒新增一本书。
2、客户端在服务端注册实时监听服务端的添加方法。
2.2、服务端具体如下:
1、新建一个监听接口,由于监听是通过AIDL接口传输调用。所以监听接口也是AIDL接口。
// IOnNewBookArrivedListener.aidl
package haibo.com.servelapp;
// Declare any non-default types here with import statements
import haibo.com.servelapp.Book;
interface IOnNewBookArrivedListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void OnNewBookArrived(in Book newBook);
}
注释:此方法为IOnNewBookArrivedListener.aidl而不是IOnNewBookArrivedListener.java。当有新书到的时候,调用OnNewBookArrived方法。
2、建立进程间通信的桥梁。在上一个例子中AIDL接口中添加两个方法,注册监听及取消监听。
// IBookManager.aidl
package haibo.com.servelapp;
// Declare any non-default types here with import statements
import haibo.com.servelapp.Book;
import haibo.com.servelapp.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
注释:第6行代码务必需要手动添加!
import haibo.com.servelapp.IOnNewBookArrivedListener;
3、修改BookManagerService服务代码,使实现接口的两个新增方法registerListener和unregisterListener。
package haibo.com.servelapp.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import haibo.com.servelapp.Book;
import haibo.com.servelapp.IBookManager;
import haibo.com.servelapp.IOnNewBookArrivedListener;
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//之所以用列表,因为每个客户端都需要注册一个监听在服务端
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mlistener = new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
private AtomicBoolean flag = new AtomicBoolean();
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 {
// if (!mlistener.contains(listener))
// mlistener.add(listener);
mListenerList.register(listener);
Log.e("BookManagerService", "registerListener success"+mListenerList.getRegisteredCallbackCount());
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if (mlistener.contains(listener))
// mlistener.remove(listener);
// flag = false;
mListenerList.unregister(listener);
Log.e("BookManagerService", "unregisterListener success"+mListenerList.getRegisteredCallbackCount());
}
};
public BookManagerService() {
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
//这种方式的缺点就是每次有个一个客户端进来,就会生成一个新的服务端线程
new Thread(new Runnable() {
@Override
public void run() {
while (!flag.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "newBook" + bookId);
Log.e("BookManagerService", "newBook" + bookId);
mBookList.add(newBook);
final int N = mListenerList.beginBroadcast();
for (int i=0;i<N;i++){
//对已经注册的所有监听者都发消息
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
listener.OnNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
}
}).start();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
}
注释:第41-56行,实现了注册监听和解除注册监听方法。
重点1:其中第24行使用的列表不是常规的List的接口。两个疑问:一是而且此处使用list而不是一个通常意义上的监听,是因为服务端可以面向多个客户端。二是使用RemoteCallbackList,因为常规的ArrayList不能实现注册监听的时候反序列化的对象和解除注册时候的反序列化对象是同一个。每次反序列化都是创建一个新对象的过程。无法解除监听。
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
4、新建线程:每五秒新建一本书。代码如下:
new Thread(new Runnable() {
@Override
public void run() {
while (!flag.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "newBook" + bookId);
Log.e("BookManagerService", "newBook" + bookId);
mBookList.add(newBook);
final int N = mListenerList.beginBroadcast();
for (int i=0;i<N;i++){
//对已经注册的所有监听者都发消息
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
listener.OnNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
}
}).start();
注释:其中第15-26行代码为查看是否有客户端注册监听,如果有,则执行客户端监听的onNewBookArrived方法:
final int N = mListenerList.beginBroadcast();
for (int i=0;i<N;i++){
//对已经注册的所有监听者都发消息
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
listener.OnNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
重点2:上述代码第一行和最后一行必须成对匹配使用。如果不使用,第一行,则会造成无法获取到监听者对象IOnNewBookArrivedListener。
2.3、客户端具体实现:
除了需要将服务端的AIDL代码完整复制到客户端外,还需要做如下操作。
1、客户端代码如下:
package haibo.com.clientapp;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.util.List;
import haibo.com.servelapp.Book;
import haibo.com.servelapp.IBookManager;
import haibo.com.servelapp.IOnNewBookArrivedListener;
public class MainActivity extends Activity {
private TextView textView,newbook;
private EditText edit_query;
private IBookManager bookManager;
private static int count = 0;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
Log.e("MainActivity","receive new book:"+msg.obj.toString());
newbook.setText("receive new book:"+msg.obj.toString());
break;
}
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
count = list.size();
textView.setText("getBookList结果是:"+list.toString());
Log.e("MainActivity",list.toString());
bookManager.registerListener(mIOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bookManager = null;
}
};
private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void OnNewBookArrived(Book newBook) throws RemoteException {
Message msg = Message.obtain();
msg.obj = newBook;
msg.what = 1;
handler.sendMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.booklist);
edit_query = (EditText) findViewById(R.id.edit_query);
newbook = (TextView) findViewById(R.id.newbook);
//初始化启动service
Intent intent = new Intent("haibo.com.servelapp.service.BookManagerService");
bindService(intent,connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
bookManager.unregisterListener(mIOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService(connection);
}
public void addBook(View view) throws RemoteException {
//添加一本书并展示
String str = edit_query.getText().toString();
if (!TextUtils.isEmpty(str)){
count++;
bookManager.addBook(new Book(count,str));
}
List<Book> newlist = bookManager.getBookList();
textView.setText("新getBookList结果是:"+newlist.toString());
Log.e("MainActivity",newlist.toString());
}
}
注释:其中第65-73行,客户端实现了接口IOnNewBookArrivedListener,表明服务端有更新的时候,客户端需要做什么。
private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void OnNewBookArrived(Book newBook) throws RemoteException {
Message msg = Message.obtain();
msg.obj = newBook;
msg.what = 1;
handler.sendMessage(msg);
}
};
第31-40行,由于服务端在子线程中执行发送消息,客户端也需要通过handler发送消息给主线程来更新UI。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
Log.e("MainActivity","receive new book:"+msg.obj.toString());
newbook.setText("receive new book:"+msg.obj.toString());
break;
}
}
};
关于注册和解除监听:
第53行:在bindService建立连接的时候注册监听。
bookManager.registerListener(mIOnNewBookArrivedListener);
第91行:在MainActivity销毁的时候解除监听,并解除绑定service。
@Override
protected void onDestroy() {
super.onDestroy();
try {
bookManager.unregisterListener(mIOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService(connection);
}
至此服务端在更新数据的时候,会主动告知已经在服务端注册监听的客户端,客户端将获得数据并更新UI。当客户端销毁的时候,会解除监听,解除服务绑定。
本章,完。AIDL,完。