这是系列文章《与Service实现双向通信》的第三篇,也是完结篇。通过前两篇介绍,我们基本已经实现了Client到Service的调用了。但是Service怎么反过来调用到我们的Client端呢?
我们接着上篇的例子,我们希望能够实现,当有用户加入(join())或者离开(leave())的时候,能够通知客户端。
实现方法是,客户端注册一个回调到Service中,当Service有用户加入或者离开的时候,就调用此回调。因为普通的interface
对象不能通过AIDL注册到Service中,我们需要定义一个AIDL接口,如下:
// IParticipateCallback.aidl
package com.race604.servicelib;
interface IParticipateCallback {
// 用户加入或者离开的回调
void onParticipate(String name, boolean joinOrLeave);
}
同时,在IRemoteService.aidl
中添加两个方法如下:
// IRemoteService.aidl
package com.race604.servicelib;
// 注意这里需要import
import com.race604.servicelib.IParticipateCallback;
interface IRemoteService {
...
void registerParticipateCallback(IParticipateCallback cb);
void unregisterParticipateCallback(IParticipateCallback cb);
}
这里需要注意的是,需要import com.race604.servicelib.IParticipateCallback;
。
我们可以先设想一下,需要在Service的实现中,用一个List来保存注册进来的IParticipateCallback
实例。你肯定很快就想到上一篇中说的问题,如果客户端意外退出的话,需要从List列表中删掉对应的实例。否则不仅浪费资源,而且在回调的时候,会出现DeadObjectException
。当然,我们可以使用上一篇文章中的方法,传入一个IBinder对象,使用Link-To-Death回调。
幸运的是,这是一个典型的应用场景,Android SDK提供一个封装好的对象:RemoteCallbackList
,帮我自动处理了Link-To-Death的问题,这就帮我们剩下了很多代码了。
下面来看我们的RemoteService
的实现:
package com.race604.remoteservice;
import ...
public class RemoteService extends Service {
...
private RemoteCallbackList<IParticipateCallback> mCallbacks = new RemoteCallbackList<>();
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
...
@Override
public void registerParticipateCallback(IParticipateCallback cb) throws RemoteException {
mCallbacks.register(cb);
}
@Override
public void unregisterParticipateCallback(IParticipateCallback cb) throws RemoteException {
mCallbacks.unregister(cb);
}
@Override
public void join(IBinder token, String name) throws RemoteException {
...
// 通知client加入
notifyParticipate(client.mName, true);
}
@Override
public void leave(IBinder token) throws RemoteException {
...
// 通知client离开
notifyParticipate(client.mName, false);
}
};
}
我们在Service中用mCallbacks
来保存回调列表,在注册和反注册IParticipateCallback
回调的时候,只要调用mCallbacks.register(cb);
和mCallbacks.unregister(cb);
即可。是不是非常容易?
然后我们来看怎么在用户加入或者退出的时候,怎么通知回调。上面的代码中,在join()
和leave()
函数中分别都调用了notifyParticipate()
函数,我们来看它的实现:
private void notifyParticipate(String name, boolean joinOrLeave) {
final int len = mCallbacks.beginBroadcast();
for (int i = 0; i < len; i++) {
try {
// 通知回调
mCallbacks.getBroadcastItem(i).onParticipate(name, joinOrLeave);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbacks.finishBroadcast();
}
这里我们也是使用一个循环,获取每个callback,然后调用onParticipate()
。循环开始前,使用mCallbacks.beginBroadcast();
,来准备开始通知Callbacks,此函数返回值是mCallbacks
中回调对象个数。循环结束的时候,调用mCallbacks.finishBroadcast();
来宣告完成。
另外,在Service销毁的时候,需要清除掉mCallbacks
中的所有的对象,如下:
@Override
public void onDestroy() {
super.onDestroy();
// 取消掉所有的回调
mCallbacks.kill();
}
客户端使用IParticipateCallback
的方法,只要实现IParticipateCallback.Stub即可,如下:
package com.race604.client;
import ...
public class MainActivity extends ActionBarActivity {
...
private ArrayAdapter<String> mAdapter;
private IParticipateCallback mParticipateCallback = new IParticipateCallback.Stub() {
@Override
public void onParticipate(String name, boolean joinOrLeave) throws RemoteException {
if (joinOrLeave) {
mAdapter.add(name);
} else {
mAdapter.remove(name);
}
}
};
}
此时,如果有用户加入或者离开,客户端就能自动受到回调了。完整的代码请参考这个Commit。
本系列文章的完整源代码托管在Github上,有需要的可以参考。
到这里,我们就实现了真正意义上的与Sevice实现双向通信。虽然写了三篇文章,但是其实也不是很复杂。本文只是涉及到Service的Binder应用。其实Binder机制是Android的精华之一,为了深入理解,可以学习一下其实现原理,可以参考大神罗升阳的《老罗的Android之旅》的系列博客,关于Binder,可以从这里开始看。
总结:
参考: