美文网首页
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

    在项目中使用AIDL绑定,注册AIDL回调时,为了兼容多客户端监听,通常用RemoteCallbackList来进...

  • Kotlin使用接口回调

    1.Java中的接口回调实现(支持多方法回调) 声明回调接口,初始化接口 使用接口回调(无参数) 使用接口回调(带...

  • AIDL的使用、传递复杂对象以及回调客户端

    前言 此教程的目的是教会大家如何使用AIDL,包括定义AIDL服务、调用AIDL服务、传递复杂对象、AIDL回调客...

  • AIDL跨进程通信

    前言 此教程的目的是教会大家如何使用AIDL,包括定义AIDL服务、调用AIDL服务、传递复杂对象、AIDL回调客...

  • Java回调深入理解

    1 接口回调 1.1 接口回调概念 什么是接口回调接口回调是指:可以把使用某一接口的类创建的对象的引用赋给该接口声...

  • Android Module之间数据传递

    方法一:使用接口回调 (1)在子module创建回调接口(参数可变) (2)在子module 实现类设置接口回调 ...

  • Android AIDL的实现

    简介 AIDL概念理解 AIDL服务端创建 客户端创建 测试结果 测试方法,结果展示 自定义接口回调,动态接收服务...

  • [转]Android 接口定义语言 (AIDL)

    Android 接口定义语言 (AIDL) Android 接口定义语言 (AIDL) 与您可能使用过的其他接口语...

  • AIDL基本使用及回调

    1.基本介绍 AIDl是Android中专门针对跨进程而设计的一种机制。AIDL全称是Android Interf...

  • Fragment向Activity传递值

    一.Fragment向Activity传值的步骤 接口回调传递(5部曲) fragment中准备回调接口 接口中...

网友评论

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

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