问题出现
最近工作遇到的问题,之前通过绑定系统签名包服务调用静默安装接口,同步获取到结果,最后将结果返回到服务器。
但是新的android系统版本出来后,静默安装接口无效了,后来找到了一个新的方案代替,还是通过aidl的方式来实现,但是执行结果只能异步获取到。
解决方案提出
通过添加一个回调接口,执行结果异步回调给调用方
疑问
跨进程的回调,怎么保证调用方和被调用方的回调接口一致?
RemoteCallbackList的使用。
看注释,Takes care of the grunt work of maintaining a list of remote interfaces,
* typically for the use of performing callbacks from a
* {@link android.app.Service} to its clients.
* typically for the use of performing callbacks from a
* {@link android.app.Service} to its clients.
管理包含了远程接口的列表,这些接口通常是用来从Service回调给调用的客户端。
/**
* Takes care of the grunt work of maintaining a list of remote interfaces,
* typically for the use of performing callbacks from a
* {@link android.app.Service} to its clients. In particular, this:
*
* <ul>
* <li> Keeps track of a set of registered {@link IInterface} callbacks,
* taking care to identify them through their underlying unique {@link IBinder}
* (by calling {@link IInterface#asBinder IInterface.asBinder()}.
* <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
* each registered interface, so that it can be cleaned out of the list if its
* process goes away.
* <li> Performs locking of the underlying list of interfaces to deal with
* multithreaded incoming calls, and a thread-safe way to iterate over a
* snapshot of the list without holding its lock.
* </ul>
*
* <p>To use this class, simply create a single instance along with your
* service, and call its {@link #register} and {@link #unregister} methods
* as client register and unregister with your service. To call back on to
* the registered clients, use {@link #beginBroadcast},
* {@link #getBroadcastItem}, and {@link #finishBroadcast}.
*
* <p>If a registered callback's process goes away, this class will take
* care of automatically removing it from the list. If you want to do
* additional work in this situation, you can create a subclass that
* implements the {@link #onCallbackDied} method.
*/
public class RemoteCallbackList<E extends IInterface> {
/*package*/ ArrayMap<IBinder, Callback> mCallbacks
= new ArrayMap<IBinder, Callback>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
private final class Callback implements IBinder.DeathRecipient {
……
}
方案测试
编写Service的aidl 接口,并另外新建一个aidl文件来写回调接口。
import com.demo.service.IServiceCallback;
interface ServiceDemoAidl {
boolean isInited();
void registerCallback(IServiceCallback cb);
void sendMSGCallback(IServiceCallback cb);
}
/**
* a callback interface used by dvbService to send
* synchronous notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*/
oneway interface IServiceCallback {
/*
* handler search message from service
*/
void handlerSearchEvent(int msgID, in List<String> strList);
}
绑定Service时,
@SuppressLint("NewApi")
private final ServiceDemoAidl.Stub mBinder = new ServiceDemoAidl.Stub() {
@Override
public void registerCallback(IServiceCallback cb) throws RemoteException {
……
}
@Override
public void <span style="font-family: Arial, Helvetica, sans-serif;">sendMSGCallback</span><span style="font-family: Arial, Helvetica, sans-serif;">(IServiceCallback cb) throws RemoteException {</span>
……
}
};
在registerCallBack里,我们使用RemoteCallBackList来注册回调接口。
static RemoteCallbackList<IServiceCallback> mCallbacks = new RemoteCallbackList<IServiceCallback>();
@Override
public void registerCallback(IServiceCallback cb) throws RemoteException {
// TODO Auto-generated method stub
if (cb != null) {
Log("mCallbacks==null?" + (mCallbacks == null) + " cb==null?" + (cb == null));
if (mCallbacks.getRegisteredCallbackCount() == 0) {
Log("mCallbacks has been killed,reregister");
mCallbacks = new RemoteCallbackList<IServiceCallback>();
}
}
}
在这个异步回调的接口中我们使用handler来模拟异步执行的情况
@Override
public void <span style="font-family: Arial, Helvetica, sans-serif;">sendMSGCallback</span><span style="font-family: Arial, Helvetica, sans-serif;">(IServiceCallback cb) throws RemoteException {</span>
// TODO Auto-generated method stub
// if (cb != null)
// mCallbacks.unregister(cb);
Log(" mHandler.sendEmptyMessage(Msg.HIDE_MSG);");
if (System.currentTimeMillis() % 2 == 0)
mHandler.sendEmptyMessage(Msg.HIDE_MSG);
else
mHandler.sendEmptyMessage(Msg.SHOW_MSG);
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
sendMsg(msg.what);
}
};
public void sendMsg(int msgId) {
ArrayList<String> strList = new ArrayList<String>();
if (msgId == Msg.SHOW_MSG) {
strList.add("ServiceDemo show msg"); // tip message
} else {
strList.add("ServiceDemo hide msg"); // tip message
}
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
try {
for (int i = 0; i < N; i++) {
mCallbacks.getBroadcastItem(i).handlerSearchEvent(msgId, strList);
}
} catch (RemoteException e) {
Log("sendMsg Exception");
}
mCallbacks.finishBroadcast();
}
Service端已经写完,开始写Client端。
service 端的两个aidl文件都直接复制到Client客户端。
进入Client客户端是绑定服务:先初始化该连接,然后绑定。
// 初始化service
private void initConnection() {
Log("initConnection");
mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mService = ServiceDemoAidl.Stub.asInterface(service);
try {
if (mService.isInited()) {
mService.registerCallback(mCallback);//注册回调接口
Log("init成功.注册回调接口");
} else {
Log("init未成功.注册回调接口");
exitService(); //
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
}
private IServiceCallback mCallback = new IServiceCallback.Stub() {
@Override
public void handlerSearchEvent(int msgID, List<String> strList) throws RemoteException {
Log("IServiceCallback handlerSearchEvent");
……
}
};
private void startService() {
Intent intent = new Intent("action");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
Button button = (Button) findViewById(R.id.bt);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
Log("unregister,mService & mCallback==" + (mService == null) + " " + (mCallback == null));
mService.<span style="font-family: Arial, Helvetica, sans-serif;">sendMSGCallback</span><span style="font-family: Arial, Helvetica, sans-serif;">(mCallback);//触发回调</span>
} catch (RemoteException e) {
Log("unregister,exception:" + e);
}
}
});
总结
实现主要使用RemoteCallBackList来管理回调接口。