返回顶部
首页 > 资讯 > 移动开发 >Android实现消息总线的几种方式详解
  • 656
分享到

Android实现消息总线的几种方式详解

2024-04-02 19:04:59 656人浏览 独家记忆
摘要

目录前言一、BroadcastReceiver 广播二、EventBus三、RxBus四、LiveDataBus五、FlowBus总结前言 消息总线又叫事件总线,为什么我们需要一个消

前言

消息总线又叫事件总线,为什么我们需要一个消息总线呢?是因为随着项目变大,页面变多,我们可能出现跨页面、跨组件、跨线程、跨进程传递消息与数据,为了更方便的直接通知到指定的页面实现具体的逻辑,我们需要消息总线来实现。

从最基本的 BroadcastReceiver 到 EventBus 再到RxBus ,后来官方出了AndroidX jetpack 我们开始使用LiveDataBus,最后到Kotlin的流行出来了FlowBus。我们看看他们是怎么一步一步演变的。

一、BroadcastReceiver 广播

我们再初入 Android 的时候都应该学过广播接收者,分为静态广播和动态注册广播,在高版本的 Android 中限制了我们一些静态广播的使用,不过我们还是能通过动态注册的方式获取一些系统的状态改变。像常用的电量变化、网络状态变化、短信发送接收的状态等等。

比如网络变化的监听:

    IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTioN);
    application.getApplicationContext().reGISterReceiver(InstanceHolder.INSTANCE, intentFilter);

在消息中线中,我们可以使用本地广播来实现 LocalBroadcastManager 消息的通知。

    LocalBroadcastManager mLocalBroadcastManager = LocalBroadcastManager.getInstance(mContext);
    
    BroadcastReceiver  mLoginReceiver = new LoginSuccessReceiver();
    mLocalBroadcastManager.registerReceiver(mLoginReceiver, new IntentFilter(Constants.ACTION_LOGIN_SUCCESS));

    private class LoginSuccessReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //刷新Home界面
            refreshHomePage();
        
            //刷新未读信息
            requestUnreadNum();
        }
    }

    //记得要解绑对应的接收器
    mLocalBroadcastManager.unregisterReceiver(mLoginReceiver);

这样就可以实现一个消息通知了。相比 EventBus 它的性能和空间的消耗都是较大的,并且只能固定在主线程运行。

二、EventBus

EventBus最大的特点就是简洁、解耦,可以直接传递我们自定义的消息Message。EventBus简化了应用程序内各组件间、组件与后台线程间的通信。记得2015年左右是非常火爆的。

EventBus的调度灵活,不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。可继承、优先级、粘滞,是 EventBus 比之于广播的优势。几乎可以满足我们全部的需求。

最初的EventBus其实就是一个方法的集合与查找,核心是通过register方法把带有@Subscrib注解的方法和参数之类的东西全部放入一个List集合,然后通过post方法去这个list循环查找到符合条件的方法去执行。

如何使用EventBus,一共分5步:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus);
        EventBus.getDefault().register(MainActivity.this);  //1.注册广播
    }
  @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(MainActivity.this); //2.解注册广播
    }

public class MessageEvent {
    public String name;

    public MessageEvent(String name) {
        this.name = name;
    }
}
    @OnClick({R.id.bt_eventbus_send_main, R.id.bt_eventbus_send_sticky})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bt_eventbus_send_main:
                //4.发送消息
                EventBus.getDefault().post(new MessageEvent("我是主页面发送过来的消息"));
                finish();
                break;
        }
    }
   
    @Subscribe(threadMode = ThreadMode.MAIN)   //主线程执行
    public void MessageEventBus(MessageEvent event) {
        //5。显示接受到的消息
        mTvEventbusResult.setText(event.name);
    }

EventBus的性能开销其实不大,EventBus2.4.0 版是利用反射来实现的,后来改成 APT 实现之后会好很多。主要问题是需要定义很多的消息对象,消息太多之后就感觉管理起来很麻烦。当消息太多之后容器内部的查找会出现性能瓶颈。

就算如此 EventBus 也是值得大家使用的。

三、RxBus

RxBus是基于RxJava实现的,强大是强大,但是学习成本比较高,需要额外导入RxJava RxAndroid等库,这些库体积还是较大的。可以实现异步的消息等。

本身的实现是很简单的:

public class RxBus {
    private volatile static RxBus mDefaultInstance;
    private final Subject<Object> mBus;

    private RxBus() {
        mBus = PublishSubject.create().toSerialized();
    }

    public static RxBus getInstance() {
        if (mDefaultInstance == null) {
            synchronized (RxBus.class) {
                if (mDefaultInstance == null) {
                    mDefaultInstance = new RxBus();
                }
            }
        }
        return mDefaultInstance;
    }

    
    public void post(Object event) {
        mBus.onNext(event);
    }

    
    public <T> Observable<T> toObservable(final Class<T> eventType) {
        return mBus.ofType(eventType);
    }

    
    public boolean hasObservers() {
        return mBus.hasObservers();
    }

    public void reset() {
        mDefaultInstance = null;
    }
}

定义消息对象:

public class MsgEvent {
    private String msg;

    public MsgEvent(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

发送与接收:

RxBus.getInstance().toObservable(MsgEvent.class).subscribe(new Observer<MsgEvent>() {
            @Override
            public void onSubscribe(Disposable d) {
                
            }

            @Override
            public void onNext(MsgEvent msgEvent) {
                //处理事件
            }

            @Override
            public void onError(Throwable e) {
                  
            }

            @Override
            public void onComplete() {

            }
        });
RxBus.getInstance().post(new MsgEvent("Java"));

缺点是容易内存泄露,我们需要使用rxlifecycle 或者使用CompositeDisposable 自己对生命周期进行处理解绑。

四、LiveDataBus

官方出了AndroidX jetpack 内部包含LiveData,它可以感知并遵循Activity、Fragment或Service等组件的生命周期。

为什么要使用LiveDataBus,正是基于LiveData对组件生命周期可感知的特点,因此可以做到仅在组件处于生命周期的激活状态时才更新UI数据。

一个简单的LiveDataBus的实现:

public final class LiveDataBus {
    private final Map<String, BusMutableLiveData<Object>> bus;
 
    private LiveDataBus() {
        bus = new HashMap<>();
    }
 
    private static class SingletonHolder {
        private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
    }
 
    public static LiveDataBus get() {
        return SingletonHolder.DEFAULT_BUS;
    }
 
    public <T> MutableLiveData<T> with(String key, Class<T> type) {
        if (!bus.containsKey(key)) {
            bus.put(key, new BusMutableLiveData<>());
        }
        return (MutableLiveData<T>) bus.get(key);
    }
 
    public MutableLiveData<Object> with(String key) {
        return with(key, Object.class);
    }
 
    private static class ObserverWrapper<T> implements Observer<T> {
 
        private Observer<T> observer;
 
        public ObserverWrapper(Observer<T> observer) {
            this.observer = observer;
        }
 
        @Override
        public void onChanged(@Nullable T t) {
            if (observer != null) {
                if (isCallOnObserve()) {
                    return;
                }
                observer.onChanged(t);
            }
        }
 
        private boolean isCallOnObserve() {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            if (stackTrace != null && stackTrace.length > 0) {
                for (StackTraceElement element : stackTrace) {
                    if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&
                            "observeForever".equals(element.getMethodName())) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
 
    private static class BusMutableLiveData<T> extends MutableLiveData<T> {
 
        private Map<Observer, Observer> observerMap = new HashMap<>();
 
        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
            super.observe(owner, observer);
            try {
                hook(observer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
 
        @Override
        public void observeForever(@NonNull Observer<T> observer) {
            if (!observerMap.containsKey(observer)) {
                observerMap.put(observer, new ObserverWrapper(observer));
            }
            super.observeForever(observerMap.get(observer));
        }
 
        @Override
        public void removeObserver(@NonNull Observer<T> observer) {
            Observer realObserver = null;
            if (observerMap.containsKey(observer)) {
                realObserver = observerMap.remove(observer);
            } else {
                realObserver = observer;
            }
            super.removeObserver(realObserver);
        }
 
        private void hook(@NonNull Observer<T> observer) throws Exception {
            //get wrapper's version
            Class<LiveData> classLiveData = LiveData.class;
            Field fieldObservers = classLiveData.getDeclaredField("mObservers");
            fieldObservers.setAccessible(true);
            Object objectObservers = fieldObservers.get(this);
            Class<?> classObservers = objectObservers.getClass();
            Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
            methodGet.setAccessible(true);
            Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
            Object objectWrapper = null;
            if (objectWrapperEntry instanceof Map.Entry) {
                objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
            }
            if (objectWrapper == null) {
                throw new NullPointerException("Wrapper can not be bull!");
            }
            Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
            Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
            fieldLastVersion.setAccessible(true);
            //get livedata's version
            Field fieldVersion = classLiveData.getDeclaredField("mVersion");
            fieldVersion.setAccessible(true);
            Object objectVersion = fieldVersion.get(this);
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion);
        }
    }
}

注册与发送:

LiveDataBus.get()
        .with("key_test", String.class)
        .observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String s) {
            }
        });

LiveDataBus.get().with("key_test").setValue(s);

LiveDataBus已经算是很好用的,自动注册解绑,根据Key传递泛型T对象,容易查找对应的接收者,也可以实现可见的触发和直接触发,可以实现跨进程,

LiveData有几点不足,只能在主线程更新数据,操作符无法转换数据,基于 Android api 实现的,换一个平台无法适应,基于这几点又开发出了FlowBus。

五、FlowBus

很多人都说Flow 的出现导致 LiveData 没那么重要了,就是因为 LiveData 的场景 都可以使用 Flow 平替了,还能更为的强大和灵活。

StateFlow 可以 替代ViewModel中传递数据,SharedFlow 可以实现事件总线。(这两者的异同如果大家有兴趣,我可以单独开一篇讲下)。

SharedFlow 就是一种热流,可以实现一对多的关系,其构造方法支持天然支持普通的消息发送与粘性的消息发送。一般我们FlowBus都是基于 SharedFlow 来实现:

object FlowBus {
    private val busMap = mutableMapOf<String, EventBus<*>>()
    private val busStickMap = mutableMapOf<String, StickEventBus<*>>()

    @Synchronized
    fun <T> with(key: String): EventBus<T> {
        var eventBus = busMap[key]
        if (eventBus == null) {
            eventBus = EventBus<T>(key)
            busMap[key] = eventBus
        }
        return eventBus as EventBus<T>
    }

    @Synchronized
    fun <T> withStick(key: String): StickEventBus<T> {
        var eventBus = busStickMap[key]
        if (eventBus == null) {
            eventBus = StickEventBus<T>(key)
            busStickMap[key] = eventBus
        }
        return eventBus as StickEventBus<T>
    }

    //真正实现类
    open class EventBus<T>(private val key: String) : LifecycleObserver {

        //私有对象用于发送消息
        private val _events: MutableSharedFlow<T> by lazy {
            obtainEvent()
        }

        //暴露的公有对象用于接收消息
        val events = _events.assharedFlow()

        open fun obtainEvent(): MutableSharedFlow<T> = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST)

        //主线程接收数据
        fun register(lifecycleOwner: LifecycleOwner, action: (t: T) -> Unit) {
            lifecycleOwner.lifecycle.addObserver(this)
            lifecycleOwner.lifecycleScope.launch {
                events.collect {
                    try {
                        action(it)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        YYLogUtils.e("FlowBus - Error:$e")
                    }
                }
            }
        }

        //协程中发送数据
        suspend fun post(event: T) {
            _events.emit(event)
        }

        //主线程发送数据
        fun post(scope: CoroutineScope, event: T) {
            scope.launch {
                _events.emit(event)
            }
        }

        //自动销毁
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroy() {
            YYLogUtils.w("FlowBus - 自动onDestroy")
            val subscriptCount = _events.subscriptionCount.value
            if (subscriptCount <= 0)
                busMap.remove(key)
        }
    }

    class StickEventBus<T>(key: String) : EventBus<T>(key) {
        override fun obtainEvent(): MutableSharedFlow<T> = MutableSharedFlow(1, 1, BufferOverflow.DROP_OLDEST)
    }
}

发送与接收消息

    // 主线程-发送消息
    FlowBus.with<String>("test-key-01").post(this@Demo11OneFragment2.lifecycleScope, "Test Flow Bus Message")
    // 接收消息
    FlowBus.with<String>("test-key-01").register(this) {
            LogUtils.w("收到FlowBus消息 - " + it)
        }

发送粘性消息

 FlowBus.withStick<String>("test-key-02").post(lifecycleScope, "Test Stick Message")
   FlowBus.withStick<String>("test-key-02").register(this){
            LogUtils.w("收到粘性消息:$it")
        }

Log如下:

总结

其实这么多消息总线框架,我更推荐EventBus LiveDataBus FlowBus这三种。

总的来说,我们尽量不依赖第三方的框架来实现,那么 FlowBus 是语言层级的,基于Kotlin的特性实现,比较推荐了,我本人是喜欢用LiveDataBus,基于 Android 开发场景来看,几乎能满足全部要求了。

如果大家有源码方面的需求可以看看这里,上面的源码也都贴出来了,不过更推荐大家根据不同的类型自行去 GitHub 上面找对应的实现封装,功能会更多,健壮性更好。

到此这篇关于Android实现消息总线的几种方式的文章就介绍到这了,更多相关Android消息总线实现内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android实现消息总线的几种方式详解

本文链接: https://lsjlt.com/news/151559.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Android实现消息总线的几种方式详解
    目录前言一、BroadcastReceiver 广播二、EventBus三、RxBus四、LiveDataBus五、FlowBus总结前言 消息总线又叫事件总线,为什么我们需要一个消...
    99+
    2024-04-02
  • Android几种消息推送方案总结
    首先看一张国内Top500 Android应用中它们用到的第三方推送以及所占数量: 现在总结下Android平台下几种推送方案的基本情况以及优缺点: 一、使用GCM(Goog...
    99+
    2022-06-06
    Android
  • 全面详解Android实现多线程的几种方式
    Android实现多线程的几种方式有:1. 使用Thread类:可以通过继承Thread类或者实现Runnable接口来创建线程类,...
    99+
    2023-09-14
    Android
  • Android如何实现消息总线
    这篇文章主要介绍“Android如何实现消息总线”,在日常操作中,相信很多人在Android如何实现消息总线问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android如何实现消息总线”的疑惑有所帮助!接下来...
    99+
    2023-07-02
  • SpringcloudBus消息总线原理是实现详解
    目录springcloud Bus什么是springcloud Bus什么是消息总线Bus实现自动刷新的原理RabbitMQ的下载配置ErlangRabbitMQBus动态刷新全局广...
    99+
    2024-04-02
  • Android富文本实现的几种方式汇总
    目录Android富文本的实现的几种方式一,Html的方式实现1.1 占位符的处理1.2 Html的占位符1.3 自定义Html标签二,Span的几种实现方式2.1 java - S...
    99+
    2024-04-02
  • Java详解实现多线程的四种方式总结
    目录前言一、四种方式实现多线程1.继承Thread类创建线程2.实现Runnable接口创建线程3.实现Callable接口4.实现有返回结果的线程二、多线程相关知识1.Runnab...
    99+
    2024-04-02
  • 详解Android中点击事件的几种实现方式
    在之前博文中多次使用了点击事件的处理实现,有朋友就问了,发现了很多按钮的点击实现,但有很多博文中使用的实现方式有都不一样,到底是怎么回事。今天我们就汇总一下点击事件的实现方式。...
    99+
    2022-06-06
    事件 Android
  • 详解Android 进程间通信的几种实现方式
    一、概述 由于应用程序之间不能共享内存。在不同应用程序之间交互数据(跨进程通讯),在Android SDK中提供了4种用于跨进程通讯的方式。 这4种方式正好对应于android...
    99+
    2022-06-06
    进程 进程间通信 通信 Android
  • Android中实现多线程操作的几种方式
    目录前言 最基础的方式 继承Thread类并实现run()方法 匿名内部类实现Runnable接口callable+FutureTask 线程池 手动创建线程池 使用Executor...
    99+
    2024-04-02
  • Spring AOP的几种实现方式总结
    Spring AOP的几种实现方式总结如下:1. 基于XML配置:在Spring配置文件中使用元素来定义切面和通知的配置,然后通过元...
    99+
    2023-08-17
    Spring AOP
  • 详解Android Activity中的几种监听器和实现方式
    目录ActivityView常用事件接口设置监听的几种方法1)让Activity实现接口2) 匿名内部类3) onClick可以在xml中设置4) 实现类5) 成员变量Activit...
    99+
    2024-04-02
  • Android 截屏实现的几种方式
    Android 截屏分为四种:View 截屏、WebView 截屏、系统截屏 和 adb 截屏 image.png image.png 1、View 截屏 View 截图是将当前 View 界面截取下来,而对于屏幕上其他信息比如:状态栏...
    99+
    2023-08-31
    android webview java
  • java 多线程的几种实现方法总结
    java 多线程的几种实现方法总结1.多线程有几种实现方法?同步有几种实现方法?多线程有两种实现方法,分别是继承Thread类与实现Runnable接口同步的实现方面有两种,分别是synchronized,wait与noti...
    99+
    2023-05-30
    java 多线程 ava
  • Android实现几种推送方式解决方案
    本文介绍在Android中实现推送方式的基础知识及相关解决方案。推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方...
    99+
    2022-06-06
    解决方案 Android
  • Android实现View滑动的几种方式
    什么是View?实现View滑动的方式有哪些? 1. 关于View我们需要知道的 (1)什么是View?     Android中的View类是...
    99+
    2022-06-06
    view Android
  • Android 实现GIF播放的几种方式
    1. Glide播放 Glide是Android上比较常见的图片加载框架,其介绍可以看Android Glide简单使用。 布局文件,GIF文件可以在ImageView里面显示 调用load()方法,导入图片并用ImageView...
    99+
    2023-08-18
    android Gif
  • java中实现多线程的几种方式
    Java多线程的使用有三种方法:继承Thread类、实现Runnable接口和使用Callable和Future创建线程。一、继承Thread类实现方式很简单,只需要创建一个类去继承Thread类然后重写run方法,在main方法中调用该类...
    99+
    2016-12-01
    java 多线程 方式
  • java 中断线程的几种方式 interrupt()详解
    中断   中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序...
    99+
    2024-04-02
  • 关于kafka发送消息的三种方式总结
    目录kafka发送消息的方式需要引入文件测试方法MAC下操作指令windows操作指令总结kafka发送消息的方式 package com.zl.kafkademo; impor...
    99+
    2023-05-14
    kafka发送消息 kafka发送 kafka消息
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作