美文网首页
AIDL使用接口回调中出现beginBroadcast() ca

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

作者: 还是做个码农 | 来源:发表于2022-09-30 10:34 被阅读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的区别需要自行了解,本篇不做过多描述。

    相关文章

      网友评论

          本文标题:AIDL使用接口回调中出现beginBroadcast() ca

          本文链接:https://www.haomeiwen.com/subject/lyozortx.html