AIDL使用接口回调中出现beginBroadcast() ca

2022-09-30  本文已影响0人  还是做个码农

在项目中使用AIDL绑定,注册AIDL回调时,为了兼容多客户端监听,通常用RemoteCallbackList来进行多客户端回调。理论上的写法如下:

private IFaceAidlCallback iFaceAidlCallback = new IFaceAidlCallback.Stub() {

        @Override
        public void onFaceStart(String content) throws RemoteException {
            try {
                int size = mRemoteCallbackList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    mRemoteCallbackList.getBroadcastItem(i).onFaceStart(content);
                }
                mRemoteCallbackList.finishBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFaceChange(String content) throws RemoteException {
            try {
                int size = mRemoteCallbackList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    mRemoteCallbackList.getBroadcastItem(i).onFaceChange(content);
                }
                mRemoteCallbackList.finishBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFaceEnd(String content) throws RemoteException {
            try {
                int size = mRemoteCallbackList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    mRemoteCallbackList.getBroadcastItem(i).onFaceEnd(content);
                }
                mRemoteCallbackList.finishBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

这种写法在正常情况下不会报错,但当传输的数据过大(回调的content数据量多)或者回调的客户端过多时,会出现上个方法为执行完finishBroadcast()而另外的方法右执行了beginBroadcast,这会导致程序抛出IllegalStateException异常。虽然try-catch使程序不会奔溃,当客户端会接收不到回调。beginBroadcast()方法源码如下:

    public int beginBroadcast() {
        synchronized (mCallbacks) {
            if (mBroadcastCount > 0) {
                throw new IllegalStateException(
                        "beginBroadcast() called while already in a broadcast");
            }
            
            final int N = mBroadcastCount = mCallbacks.size();
            if (N <= 0) {
                return 0;
            }
            Object[] active = mActiveBroadcast;
            if (active == null || active.length < N) {
                mActiveBroadcast = active = new Object[N];
            }
            for (int i=0; i<N; i++) {
                active[i] = mCallbacks.valueAt(i);
            }
            return N;
        }
    }

解决办法如下:

 private Lock mLock = new ReentrantLock();

 private IFaceAidlCallback iFaceAidlCallback = new IFaceAidlCallback.Stub() {

        @Override
        public void onFaceStart(String content) throws RemoteException {
            mLock.lock();
            try {
                int size = mRemoteCallbackList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    mRemoteCallbackList.getBroadcastItem(i).onFaceStart(content);
                }
                mRemoteCallbackList.finishBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }

        @Override
        public void onFaceChange(String content) throws RemoteException {
            mLock.lock();
            try {
                int size = mRemoteCallbackList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    mRemoteCallbackList.getBroadcastItem(i).onFaceChange(content);
                }
                mRemoteCallbackList.finishBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }

        @Override
        public void onFaceEnd(String content) throws RemoteException {
            mLock.lock();
            try {
                int size = mRemoteCallbackList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    mRemoteCallbackList.getBroadcastItem(i).onFaceEnd(content);
                }
                mRemoteCallbackList.finishBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mLock.unlock();
            }
        }
    };

采用加锁的方式,当执行方法时进行阻塞,保证方法同步。至于为什么不用synchronized同步的方式来保证方法的同步,需要另外的讨论。之前遇到过 synchronized同步方法还是会报错的情况,故采用ReentrantLock加锁的方式来保证方法同步,ReentrantLock和synchronized的区别需要自行了解,本篇不做过多描述。

上一篇 下一篇

猜你喜欢

热点阅读