跨应用的aidl异步回调

问题出现

最近工作遇到的问题,之前通过绑定系统签名包服务调用静默安装接口,同步获取到结果,最后将结果返回到服务器。

但是新的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.
 
管理包含了远程接口的列表,这些接口通常是用来从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);

        }
 
定义Handler
 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();
    }
 
回调接口的调用需要用 mCallbacks.getBroadcastItem(i).handlerSearchEvent(msgId, strList)的方式来执行。
 
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来管理回调接口。
 


 
 
 

 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值