上一篇博文中,实现了简单的系统服务的回调。在这个简单化的实例中,回调有如下几个明显的缺点:
- 只支持注册一个listener。
- 注销的时候不能指定注销的listener。
- 回调的时候可能会因为APP没有及时处理导致服务卡住。
在这篇博文中,我们将逐步解决这几个问题。
SystemManager中的注册与注销
我们先来看一下上一篇博文中所实现的SystemManager中的注册函数与注销函数:
/**
* 注册回调。
*
* @param listener
* @return 成功返回0,失败请参考错误码ErrorCode
*/
public int registerListener(SystemManagerListener listener) {
try {
SystemManagerListenerTransfer transfer = new SystemManagerListenerTransfer(listener);
return mService.registerListener(transfer);
} catch (RemoteException e) {
e.printStackTrace();
return SystemManagerCons.ErrorCode.ERROR_REMOTE_EXCEPTION;
}
}
/**
* 注销回调。
*
* @param listener 回调。
* @return 注销成功返回0,否则返回ErrorCode。
*/
public int unregisterListener() {
try {
return mService.unregisterListener();
}
} catch (RemoteException e) {
e.printStackTrace();
return SystemManagerCons.ErrorCode.ERROR_REMOTE_EXCEPTION;
}
}
在注册函数中,我们是用SystemManagerListener构造了一个SystemManagerListenerTransfer并将这个Transfer传入了Service,那么,在注销的时候,如果我们又new一个SystemManagerListenerTransfer并传入Service,那么对于Service来说,两次传入的是两个对象,无法进行正确的注销。
所以,我们需要将第一次new出来的Transfer缓存下来,当需要注销的时候,根据Listener查找对应的Transfer传入Service进行注销即可。为了快速查询,我们是用java中的map。代码如下:
// 声明一个HashMap对象来缓存new出来的Transfer。
private HashMap<SystemManagerListener, SystemManagerListenerTransfer> mCachedSystemManagerListener;
public SystemManager(Context context, ISystemManager service) {
mContext = context;
mService = service;
// 初始化这个HashMap。
this.mCachedSystemManagerListener = new HashMap<SystemManagerListener, SystemManagerListenerTransfer>();
}
/**
* 注册回调。
*
* @param listener
* @return 成功返回0,失败请参考错误码ErrorCode
*/
public int registerListener(SystemManagerListener listener) {
Log.d(TAG, "registerListener() is called");
if (mService == null) {
Log.d(TAG, "mService is null, return.");
return SystemManagerCons.ErrorCode.ERROR_SERVICE_NULL;
}
// 检查非空。
if (listener == null) {
Log.d(TAG, "listener is null, error.");
return SystemManagerCons.ErrorCode.ERROR_NULL_OBJECT;
}
// 检查是否为重复注册。
if (this.mCachedSystemManagerListener.containsKey(listener)) {
Log.d(TAG, "this listener is already registered");
return SystemManagerCons.ErrorCode.ERROR_LISTENER_ALREADY_REGISTERED;
}
try {
SystemManagerListenerTransfer transfer = new SystemManagerListenerTransfer(listener);
// 把new出来的Transfer缓存下来。
this.mCachedSystemManagerListener.put(listener, transfer);
return mService.registerListener(transfer);
} catch (RemoteException e) {
e.printStackTrace();
return SystemManagerCons.ErrorCode.ERROR_REMOTE_EXCEPTION;
}
}
/**
* 注销回调。
*
* @param listener 回调。
* @return 注销成功返回0,否则返回ErrorCode。
*/
public int unregisterListener(RadioManagerListener listener) {
Log.d(TAG, "unregisterListener() is called");
if (mService == null) {
Log.d(TAG, "mService is null, return.");
return SystemManagerCons.ErrorCode.ERROR_SERVICE_NULL;
}
// 检查非空
if (listener == null) {
Log.d(TAG, "listener is null, error.");
return SystemManagerCons.ErrorCode.ERROR_NULL_OBJECT;
}
try {
// 在缓存中根据Listener查找Transfer
SystemManagerListenerTransfer transfer = this.mCachedSystemManagerListener
.remove(listener);
if (transfer == null) { // 如果还没有被注册过
Log.d(TAG, "listener has not been registered ye");
return SystemManagerCons.ErrorCode.ERROR_LISTENER_NOT_REGISTERED;
} else {
return mService.unregisterListener(transfer);
}
} catch (RemoteException e) {
e.printStackTrace();
return SystemManagerCons.ErrorCode.ERROR_REMOTE_EXCEPTION;
}
}
SystemManagerService中的注册与注销
要在SystemManagerService中支持同时注册多个Listener,就需要维护一个Listener的列表,注册的时候,将Listener加入这个列表中,注销的时候,将Listener从列表中删掉就行了。代码如下:
// 声明这个List。
private List<ISystemManagerListener> mSystemManagerListenerList;
// 在Service的构造函数里初始化List
public SystemManagerService(Context context) {
this.mContext = context;
this.mSystemManagerListenerList = new LinkedList<ISystemManagerListener>();
}
@Override
public int registerListener(ISystemManagerListener listener) throws RemoteException {
Log.d(TAG, "registerListener() is called");
if (mSystemManagerListenerList.contains(listener)) { // 已经注册过
Log.d(TAG, "this listener has already been registered");
return SystemManagerCons.ErrorCode.ERROR_LISTENER_ALREADY_REGISTERED;
} else {
mSystemManagerListenerList.add(listener);
return SystemManagerCons.ErrorCode.ERROR_NO_ERROR;
}
}
@Override
public int unregisterListener(ISystemManagerListener listener) throws RemoteException {
Log.d(TAG, "unregisterListener() is called");
if (mSystemManagerListenerList.remove(listener)) {
return SystemManagerCons.ErrorCode.ERROR_NO_ERROR;
} else {
return SystemManagerCons.ErrorCode.ERROR_LISTENER_NOT_REGISTERED;
}
}
调用过程的优化
在APP实现的Listener中,实现的函数有可能会有一些耗时的操作,如果我们直接调用这些函数,将导致服务的主线程被阻塞,这显然是不合理的。所以我们需要使用一个子线程来完成调用APP的Listener中函数的任务。为了方便主线程与子线程之间的交互,我们使用一个HandlerThread即可。HandlerThread的使用方法在拙作 Thread、Handler与HandlerThread 里已经提到过,这里直接贴出代码:
private SystemHandlerThread mSystemHandlerThread;
private Handler threadHandler;
public SystemManagerService(Context context) {
this.mContext = context;
this.mSystemManagerListenerList = new LinkedList<ISystemManagerListener>();
mSystemHandlerThread = new SystemHandlerThread(TAG);
mSystemHandlerThread.start();
threadHandler = new Handler(mSystemHandlerThread.getLooper(), mSystemHandlerThread);
}
public SystemManagerService(Context context) {
this.mContext = context;
this.mSystemManagerListenerList = new LinkedList<ISystemManagerListener>();
mSystemHandlerThread = new SystemHandlerThread(TAG);
mSystemHandlerThread.start();
threadHandler = new Handler(mSystemHandlerThread.getLooper(), mSystemHandlerThread);
}
private class SystemHandlerThread extends HandlerThread implements Callback {
public SystemHandlerThread(String name) {
super(name);
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
default:
case MSG_PROGRESS:
precessProgress(msg.arg1);
break;
}
return true;
}
private void precessProgress(int progress) {
}
}
当从jni中收到数据之后,像下面这样发送一个消息给HandlerThread即可。
threadHandler.obtainMessage(MSG_PROGRESS, progress, 0).sendToTarget();
在HandlerThread的precessProgress()函数中,在正常的情况下,只需要遍历Listener的List,依次调用Listener的onProgress()函数即可,但是如果一个APP退出了(有可能是异常导致崩溃),但是没有主动去调用注销Listener的函数,那么这个Listener的List中依然会有这个APP的Listener。在这种情况下,我们调用其onProgress()函数会发生什么呢?答案是会发生RemoteException。所以我们需要在发生RemoteException的时候,将这个Listener从List中删掉,就能防止这个List无限制增长。另外,在遍历的过程中改变List的元素也有一些小的技巧。代码如下:
private void precessProgress(int progress) {
for (Iterator<ISystemManagerListener> it = mSystemManagerListenerList.iterator(); it
.hasNext();) {
ISystemManagerListener l = it.next();
try {
l.onProgress(progress);
} catch (RemoteException e) {
Log.d(TAG, "RemoteException occured when call onScanFrequency");
it.remove();
}
}
}
至此,就解决了本文开头提到的三个问题。