Android开发经验谈

关于AIDL 服务端向客户端传输数据问题

2018-04-25  本文已影响75人  捉影T_T900

最近有个需求,用到了AIDL来进行跨进程通信,关于AIDL,我们常规的操作是客户端向服务端发送请求,这很简单,只需要在Stub上调用定义的方法即可,顶多需要执行方法的返回值。
但是坑爹的需求总是那么多,例如我需要反向操作,服务器端向客户端发送数据。整个Binder的模型有点像网络的DNS模型,既然能连通,那应该是可以反着走的。

回想一下观察者模式,被观察者持有观察者的一个接口回调,当被观察者数据变化时,通过该接口通知外部的观察者数据的变化情况,主体思路就是这样。

注意的内容是什么呢?
1、由于我们是进行了跨进程的操作,定义的回调接口理论上不属于AIDL默认支持的数据类型,所以要新建一个AIDL文件
2、原先的Stub类需要注册这个回调接口
3、关注 RemoteCallbackList 这个类,它是专门用于删除跨进程listener的接口,它是一个泛型,支持管理任意的AIDL接口

上核心代码:
1、新建listener的AIDL

interface IOnWebSocketListener {

    void onSocketConnected();

    void onReceiveServerMessage(in CMDEntity aidlCMDEntity);

    void onSocketClose();

    void onSocketError();
}

2、原有的AIDL注册listener

import com.ideacode.test.IOnWebSocketListener;

interface IWebSocketAidlInterface {

    void registListener(IOnWebSocketListener listener);

    void unregistListener(IOnWebSocketListener listener);
}

3、客户端要做的事情

private IWebSocketAidlInterface webSocketAidlInterface;

IOnWebSocketListener listener = new IOnWebSocketListener.Stub() {
        @Override
        public void onSocketConnected() throws RemoteException {
            Logger.e("连接成功");
        }

        @Override
        public void onReceiveServerMessage(CMDEntity aidlCMDEntity) throws RemoteException {
            Logger.e("收到消息:" + aidlCMDEntity.getMessage());
        }

        @Override
        public void onSocketClose() throws RemoteException {
            Logger.e("连接关闭");
        }

        @Override
        public void onSocketError() throws RemoteException {
            Logger.e("连接异常");
        }
    };

ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            webSocketAidlInterface = IWebSocketAidlInterface.Stub.asInterface(iBinder);
           
            try {
                webSocketAidlInterface.registListener(listener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            webSocketAidlInterface = null;
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (serviceConnection != null && webSocketAidlInterface.asBinder().isBinderAlive()) {
            try {
                webSocketAidlInterface.unregistListener(listener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            unbindService(serviceConnection);
        }
    }

4、服务端要做的事情

private RemoteCallbackList<IOnWebSocketListener> mListener = new RemoteCallbackList<>();

IWebSocketAidlInterface.Stub mBinder = new IWebSocketAidlInterface.Stub() {

        @Override
        public void registListener(IOnWebSocketListener listener) throws RemoteException {
            mListener.register(listener);
            WebSocketManager webSocketManager = WebSocketManager.getInstance();
            webSocketManager.registCallback(mListener);
        }

        @Override
        public void unregistListener(IOnWebSocketListener listener) throws RemoteException {
            mListener.unregister(listener);
            WebSocketManager webSocketManager = WebSocketManager.getInstance();
            webSocketManager.unregistCallback(mListener);
        }
    };

5、在需要的位置进行listener的响应

private RemoteCallbackList<IOnWebSocketListener> callbackList;

                @Override
                public void onOpen(ServerHandshake handshakedata) {
                    //连接成功
                    Logger.i("websoeket opened connection");
                    int n = callbackList.beginBroadcast();
                    for (int i = 0; i < n; i++) {
                        IOnWebSocketListener listener = callbackList.getBroadcastItem(i);
                        if (listener != null) {
                            try {
                                listener.onSocketConnected();
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    callbackList.finishBroadcast();
                }

注意的地方:
1、RemoteCallbackList这个东西并不是一个List,所以不能进行foreach遍历
2、beginBroadcast和finishBroadcast要成堆出现,哪怕是要获取RemoteCallbackList中的元素个数。

这样客户端就能收到服务器端回调过来的消息啦,实测通过,比我之前用的方案更清晰,很好很好!!!

上一篇下一篇

猜你喜欢

热点阅读