AIDL使用接口回调中出现beginBroadcast() called while already in a broadcast的错误

在项目中使用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的区别需要自行了解,本篇不做过多描述。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

言并肃

感谢大哥支持!您的鼓励是我动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值