美文网首页Android组件化
Android组件化架构 - 2. 组件间通信机制

Android组件化架构 - 2. 组件间通信机制

作者: 今阳说 | 来源:发表于2020-12-18 13:47 被阅读0次

    本地广播LocalBroadcastManager

    说到组件间通信第一个肯定想到广播BroadcastReceiver,但是这里要说的是一个更优的选择---本地广播LocalBroadcastManager;

    • 优点:只在app内传播, 信息不会泄露,也不会被别人的广播干扰, 且比全局广播更高效;

    • 缺点:但是本地广播传输消息时将一切都交给系统负责,无法干预传输中的步骤;

    • 使用观察者模式

    使用demo:

    class LocalBroadcastActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_local_broadcast)
            testLocalBroadcast()
        }
    
        private lateinit var broadcastReceiver: BroadcastReceiver
        private lateinit var lbm: LocalBroadcastManager
        private val localAction = "com.ljy.publicdemo.localAction"
        private fun testLocalBroadcast() {
            broadcastReceiver = object : BroadcastReceiver() {
                /**
                 * 接收并出列广播
                 */
                override fun onReceive(context: Context?, intent: Intent?) {
                    if (localAction == intent!!.action) {
                        val value = intent.getStringExtra("key_001")
                        LogUtils.d("key_001=$value, do something... ")
                    }
                }
            }
            //创建
            lbm = LocalBroadcastManager.getInstance(this)
            //注册
            lbm.registerReceiver(broadcastReceiver, IntentFilter(localAction))
        }
    
        private fun sendBroadcast() {
            //发送
            val intent = Intent(localAction)
            intent.putExtra("key_001", "value_001")
            lbm.sendBroadcast(intent)
        }
        
        fun onBtnClick(view: View) {
            when (view.id) {
                R.id.button_send_broadcast -> sendBroadcast()
            }
        }
    
        override fun onDestroy() {
            super.onDestroy()
            //解绑
            lbm.unregisterReceiver(broadcastReceiver)
        }
    }
    

    本质:看一下LocalBroadcastManager源码

    //构造方法如下,其本质还是用handler通信
    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }
    
    //内部类ReceiverRecord:将receiver和intentFilter封装成ReceiverRecord对象,
    private static final class ReceiverRecord {
            final IntentFilter filter;
            final BroadcastReceiver receiver;
            boolean broadcasting;
            boolean dead;
    
            ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
                filter = _filter;
                receiver = _receiver;
            }
    }
    
    //内部类BroadcastRecord:将ReceiverRecord对象封装在BroadcastRecord对象中
    private static final class BroadcastRecord {
        final Intent intent;
        final ArrayList<ReceiverRecord> receivers;
    
        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }
    
    //有三个集合
    //一个以receiver为key、以ReceiverRecord列表为value的map
    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers= new HashMap<>();
    //一个以action为key,以ReceiverRecord列表为value的map
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    //BroadcastRecord对象的集合
    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
    
    //注册和解绑的源码就不贴了
    //registerReceiver()就是装填mReceivers,mActions这两个map
    //unregisterReceiver()就是清除mReceivers,mActions
    
    //发送广播源码
    public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
            //。。。省略部分源码,主要就是对intent中的信息进行校验
            //最关键的是下面的装填mPendingBroadcasts,和发送handler消息
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }
    
    //还有一个同步发送广播的方法
     public void sendBroadcastSync(@NonNull Intent intent) {
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
    }
    
    //上面源码看出一个至关重要的方法executePendingBroadcasts,才是真正调用广播处理的回调
     void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i<brs.length; i++) {
                final BroadcastRecord br = brs[i];
                final int nbr = br.receivers.size();
                for (int j=0; j<nbr; j++) {
                    final ReceiverRecord rec = br.receivers.get(j);
                    if (!rec.dead) {
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }
    

    事件总线

    • 组件化层级障碍:

    由组件化架构图可以看出,组件间相互独立,没有依赖,也就没有关系,也就无法传递信息,那么要如何交流呢?

    这时就需要用到基础层base module, 因为组件层的模块都依赖于基础层;

    • 事件总线

    Android中activity,fragment,service间信息传递相对复杂, 如果用系统级别的广播, 有耗时,容易被捕捉,无法干预,可传输数据类型少等弊端,
    于是大佬们就搞出了事件总线;

    下面是三种目前常用的事件总线框架:

    1. EventBus:

    一款针对Android优化的发布/订阅事件总线,主要功能是替代handler,intent,broadcast;
    优点是开销小,代码优雅,将发送者和接收者解耦;

    /**
     * 演示eventBus的使用
     */
    
    //1. 定义要传递的事件实体
    data class TestEvent(val params: String)
    
    class EventBusActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_eventbus)
        }
    
        //2. 准备订阅者
        @Subscribe(threadMode = ThreadMode.MAIN)
        fun onMessageEvent(event: TestEvent) {
            LjyLogUtil.d(event.params)
        }
    
        //3. 注册订阅者
        override fun onStart() {
            super.onStart()
            EventBus.getDefault().register(this)
        }
    
        //4. 解绑订阅者
        override fun onStop() {
            super.onStop()
            EventBus.getDefault().unregister(this)
        }
    
        fun onBtnClick(view: View) {
            when (view.id) {
                //5. 发送事件, 实际场景中发送事件的发送者一般在其他页面或模块中
                R.id.btn_send_event -> EventBus.getDefault().post(TestEvent("测试一下事件发送"))
            }
        }
    
    }
    
    EventBus2.x的版本和3.x是有很大区别的:
    1. 2.x使用的是运行时注解,采用了反射的方式对整个注册的类的所有方法进行扫描来完成注册,因而会对性能有一定影响;
    2. 3.x使用的是编译时注解,Java文件会编译成.class文件,再对class文件进行打包等一系列处理。在编译成.class文件时,
    EventBus会使用EventBusAnnotationProcessor注解处理器读取@Subscribe()注解并解析、处理其中的信息,
    然后生成Java类来保存所有订阅者的订阅信息。这样就创建出了对文件或类的索引关系,并将其编入到apk中。
    3. 从EventBus3.0开始使用了对象池缓存减少了创建对象的开销。
    
    EventBus给Android开发者世界带来了一种新的框架和思想,就是消息的发布和订阅。这种思想在其后很多框架中都得到了应用。
    
    2. RxBUs

    RxBus不是一个库,而是一个文件,实现只有短短30行代码。RxBus本身不需要过多分析,它的强大完全来自于它基于的RxJava技术。

    public class RxBus {
    
        /**
         * Instance of {@link Bus}
         */
        private static Bus sBus;
    
        /**
         * Get the instance of {@link Bus}
         *
         * @return
         */
        public static synchronized Bus get() {
            if (sBus == null) {
                sBus = new Bus(ThreadEnforcer.ANY);
            }
            return sBus;
        }
    }
    
    RxBus有很多实现,如:
    AndroidKnife/RxBus(https://github.com/AndroidKnife/RxBus)(即上面的代码)
    Blankj/RxBus(https://github.com/Blankj/RxBus)
    
    下面是使用rxbus的demo
    //1. rxbus的实现类
    package com.ljy.publicdemo.util;
    
    import androidx.lifecycle.Lifecycle;
    import androidx.lifecycle.LifecycleOwner;
    
    import com.trello.lifecycle2.android.lifecycle.AndroidLifecycle;
    import com.trello.rxlifecycle2.LifecycleProvider;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import io.reactivex.Observable;
    import io.reactivex.ObservableEmitter;
    import io.reactivex.ObservableOnSubscribe;
    import io.reactivex.subjects.PublishSubject;
    import io.reactivex.subjects.Subject;
    
    /**
     * @Author: LiuJinYang
     * @CreateDate: 2020/5/11 17:01
     */
    public class RxBusUtil {
        private volatile static RxBusUtil mDefaultInstance;
        private final Subject<Object> mBus;
    
        private final Map<Class<?>, Object> mStickyEventMap;
    
        private RxBusUtil() {
            mBus = PublishSubject.create().toSerialized();
            mStickyEventMap = new ConcurrentHashMap<>();
        }
    
        public static RxBusUtil getInstance() {
            if (mDefaultInstance == null) {
                synchronized (RxBusUtil.class) {
                    if (mDefaultInstance == null) {
                        mDefaultInstance = new RxBusUtil();
                    }
                }
            }
            return mDefaultInstance;
        }
    
        /**
         * 发送事件
         */
        public void post(Object event) {
            mBus.onNext(event);
        }
    
        /**
         * 使用Rxlifecycle解决RxJava引起的内存泄漏
         */
        public <T> Observable<T> toObservable(LifecycleOwner owner, final Class<T> eventType) {
            LifecycleProvider<Lifecycle.Event> provider = AndroidLifecycle.createLifecycleProvider(owner);
            return mBus.ofType(eventType).compose(provider.<T>bindToLifecycle());
        }
    
        /**
         * 判断是否有订阅者
         */
        public boolean hasObservers() {
            return mBus.hasObservers();
        }
    
        public void reset() {
            mDefaultInstance = null;
        }
    
    
        /**
         * Stciky 相关
         */
    
        /**
         * 发送一个新Sticky事件
         */
        public void postSticky(Object event) {
            synchronized (mStickyEventMap) {
                mStickyEventMap.put(event.getClass(), event);
            }
            post(event);
        }
    
        /**
         * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
         * 使用Rxlifecycle解决RxJava引起的内存泄漏
         */
        public <T> Observable<T> toObservableSticky(LifecycleOwner owner, final Class<T> eventType) {
            synchronized (mStickyEventMap) {
                LifecycleProvider<Lifecycle.Event> provider = AndroidLifecycle.createLifecycleProvider(owner);
                Observable<T> observable = mBus.ofType(eventType).compose(provider.<T>bindToLifecycle());
                final Object event = mStickyEventMap.get(eventType);
    
                if (event != null) {
                    return observable.mergeWith(Observable.create(new ObservableOnSubscribe<T>() {
                        @Override
                        public void subscribe(ObservableEmitter<T> subscriber) throws Exception {
                            subscriber.onNext(eventType.cast(event));
                        }
                    }));
                } else {
                    return observable;
                }
            }
        }
    
        /**
         * 根据eventType获取Sticky事件
         */
        public <T> T getStickyEvent(Class<T> eventType) {
            synchronized (mStickyEventMap) {
                return eventType.cast(mStickyEventMap.get(eventType));
            }
        }
    
        /**
         * 移除指定eventType的Sticky事件
         */
        public <T> T removeStickyEvent(Class<T> eventType) {
            synchronized (mStickyEventMap) {
                return eventType.cast(mStickyEventMap.remove(eventType));
            }
        }
    
        /**
         * 移除所有的Sticky事件
         */
        public void removeAllStickyEvents() {
            synchronized (mStickyEventMap) {
                mStickyEventMap.clear();
            }
        }
    }
    
    //2. 使用
    class RxBusActivity:AppCompatActivity(){
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_rx_bus)
            registerRxBus()
        }
    
        private fun registerRxBus() {
            RxBusUtil.getInstance().toObservable(this, TestEvent::class.java).subscribe(object : Observer<TestEvent> {
    
                override fun onSubscribe(d: Disposable) {
                    LjyLogUtil.d("onSubscribe:回调返回Disposable 对象,让观察者管理订阅状态, 例如取消订阅")
                }
    
                override fun onNext(event: TestEvent) {
                    LjyLogUtil.d("onNext:数据接收处理")
                    LjyLogUtil.d(event.params)
                }
    
                override fun onError(e: Throwable) {
                    LjyLogUtil.d("onError:发生异常,终止事件流")
                }
    
                override fun onComplete() {
                    LjyLogUtil.d("onComplete:事件流结束")
                }
            })
        }
    
        fun onBtnClick(view: View) {
            when(view.id){
                R.id.btn_rx_bus->RxBusUtil.getInstance().post(TestEvent("测试rxBus发送消息"))
            }
        }
    }
    
    //因为用了Rxlifecycle,推出activity时无需解绑
    
    

    与EventBus相比的优点,其实也就是rxJava的优点:
    1、RxJava的Observable有onError、onComplete等状态回调。
    2、RxJava使用组合而非嵌套的方式,避免了回调地狱。
    3、RxJava的线程调度设计的更加优秀,更简单易用。
    4、RxJava可使用多种操作符来进行链式调用来实现复杂的逻辑。
    5、RxJava的信息效率高于EventBus2.x,低于EventBus3.x。

    那么技术选型时如何取舍呢?如果项目中使用了RxJava,则使用RxBus,否则使用EventBus3.x

    3. LiveDataBus

    LiveDataBus是基于LiveData实现的类似EventBus的消息通信框架,它是基于LiveData实现的,完全可以代替EventBus,RxBus;

    为什么会有他呢?
    Handler : 容易导致内存泄漏,空指针,高耦合,不利于维护
    EventBus :原理实现复杂,无法混淆,需要手动绑定生命周期
    RxBus:依赖于RxJava,包太大,影响apk大小,app启动时间

    //初代实现如下
    ublic final class LiveDataBus {
    
        private final Map<String, MutableLiveData<Object>> bus;
    
        private LiveDataBus() {
            bus = new HashMap<>();
        }
    
        private static class SingletonHolder {
            private static final LiveDataBus DATA_BUS = new LiveDataBus();
        }
    
        public static LiveDataBus get() {
            return SingletonHolder.DATA_BUS;
        }
    
        public <T> MutableLiveData<T> getChannel(String target, Class<T> type) {
            if (!bus.containsKey(target)) {
                bus.put(target, new MutableLiveData<>());
            }
            return (MutableLiveData<T>) bus.get(target);
        }
    
        public MutableLiveData<Object> getChannel(String target) {
            return getChannel(target, Object.class);
        }
    }
    //使用
    注册订阅:
    LiveDataBus.get().getChannel("key_test", Boolean.class)
            .observe(this, new Observer<Boolean>() {
                @Override
                public void onChanged(@Nullable Boolean aBoolean) {
                }
            });
    发送消息:
    LiveDataBus.get().getChannel("key_test").setValue(true);
    
    

    其项目地址如下:
    https://github.com/JinYangLiu/LiveEventBus
    另外的项目SmartEventBus,基于LiveEventBus实现,能让你定制自己的消息总线
    https://github.com/JinYangLiu/SmartEventBus

    4. 组件化事件总线的考量

    其实目前常用的各种事件总线xxBus原理都差不多

    那么在组件化项目中如何使用这些事件总线呢

    1.EventBus,RxBus: 将xxEvent消息容器和事件总线框架的依赖放到base module,其他模块组件依赖于base module;
    但是这样每个模块改动都需要增删改baseModule中的消息容器, 组件化要求功能模块独立, 各组件应该尽量避免影响base module;

    1. LiveDataBus: 无需建立消息模型,但无法想前两者一样拥有类名索引,无法引导正确的编写代码,也无法传递自定义实体到其他模块;
    2. 组件化中使用EventBus,RxBus,为了更大程度的解耦,可以独立出一个事件总线module,添加事件的实体都在这个module中,
      base module依赖 这个事件总线module对事件通信的解耦, 抽离事件到事件总线module中减少对base module的影响;
    我是今阳,如果想要进阶和了解更多的干货,欢迎关注公众号”今阳说“接收我的最新文章

    相关文章

      网友评论

        本文标题:Android组件化架构 - 2. 组件间通信机制

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