我的天空 我的梦

自由翱翔在追求理想的浩瀚天空当中

如何更好地使用EventBus

EventBus是Android常用的事件总线之一,EventBus基于观察者模式,可以很好的解耦两个类之间的通信(一般都是采用注册监听器进行回调的方式),例如,Fragment和Activity之间的通信,底层功能与界面之间的通信等等。本文不会介绍EventBus的使用,主要是通过一个例子,给出EventBus使用中可能出现的一个问题,然后给出一种更加合适的使用EventBus的建议。


EventBus通过识别事件类型来找到该事件类型的订阅者,并将消息发送给订阅者,大家是否注意到,如果在内存中存在同一事件类型的两个订阅者,这两个订阅者都会收到该消息,那么,如何区分这个消息是否是该订阅者关注的?看一个有问题的示例,首先是MainActivity:

data class ShowToastEvent(val content: String)

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val container1 = supportFragmentManager.findFragmentById(R.id.container1)
    if (container1 == null) {
        supportFragmentManager.beginTransaction()
                .add(R.id.container1, FirstFragment())
                .commit()
    }

    val container2 = supportFragmentManager.findFragmentById(R.id.container2)
    if (container2 == null) {
        supportFragmentManager.beginTransaction()
                .add(R.id.container2, SecondFragment())
                .commit()
    }
}

定义了一个ShowToastEvent的数据类作为EventBus的事件类型,只有一个属性content,传递希望显示的字符串。


包含了两个Frgment(FirstFragment和SecondFragment),这两个Fragment很简单,就是两个空白的Fragment,这里不再介绍。


MainActivity有一个菜单:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/show_toast1"
        android:title="显示Toast1"
        app:showAsAction="never"/>

    <item android:id="@+id/show_toast2"
        android:title="显示Toast2"
        app:showAsAction="never"/>
</menu>


菜单的创建和处理:

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main_activity, menu)
    return true
}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) {
        R.id.show_toast1 ->
            EventBus.getDefault()
                    .post(ShowToastEvent("Toast1"))
        R.id.show_toast2 ->
            EventBus.getDefault()
                    .post(ShowToastEvent("Toast2"))
        else -> return super.onOptionsItemSelected(item)
    }

    return true
}

就是点击不同的菜单项传递不同的Toast内容。接下来看看如何订阅和处理事件:

override fun onStart() {
    super.onStart()
    EventBus.getDefault().register(this)
}

override fun onStop() {
    EventBus.getDefault().unregister(this)
    super.onStop()
}

@Subscribe
fun onToastShow(event: MainActivity.ShowToastEvent) {
    Toast.makeText(context, "From First ${event.content}", Toast.LENGTH_SHORT).show()
}

这个是FirstFragment的处理,SecondFragment与之类似,只是将onToastShow中的From First换为From Second。运行起来看一下,可以发现,点击显示Toast1菜单项,会先后出现From First Toast1和From Second Toast1两个Toast。


那么如何让我们的FirstFragment和SecondFragment分别处理两个菜单点击事件呢?提供两种方法,第一种方法,修改事件数据类

data class ShowToastEvent(val targetCls:Class<out Fragment>,
                          val content: String)

添加一个参数,作为事件发送的目的类,如果一个类的不同对象对事件处理不同,这里可以换成传递实际的对象作为target。然后,需要修改抛出事件的代码:

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) {
        R.id.show_toast1 ->
            EventBus.getDefault()
                    .post(ShowToastEvent(FirstFragment::class.java, "Toast1"))
        R.id.show_toast2 ->
            EventBus.getDefault()
                    .post(ShowToastEvent(SecondFragment::class.java, "Toast2"))
        else -> return super.onOptionsItemSelected(item)
    }

    return true
}

最后是订阅的代码:

@Subscribe
fun onToastShow(event: MainActivity.ShowToastEvent) {
    if (event.targetCls == this::class.java) {
        Toast.makeText(context, "From First ${event.content}", Toast.LENGTH_SHORT).show()
    }
}

可以看到,我们可以检查是否target是我们需要处理的,如果target匹配,我们才会处理。这样,就不会存在处理自己不关心的事件的问题。记得相应调整SecondFragment的代码。


但是,这样做好吗?很不好,这样做,增加了Activity和Fragment的耦合,如果将来我们会修改FirstFragment而使用AnotherFragment,我们需要修改Activity发送事件的代码,还有一种情况,如果观察者不是Fragment了(当然可以改Class<?>解决这类问题,但是很不好),又该怎么办?所以,这里引出了第二种方式,首先修改事件类:

data class ShowToastEvent(val type:EventType, val content: String) {
    enum class EventType {
        TOAST1, TOAST2
    }
}

相当于MainActivity昭告天下,我可以抛出事件ShwoToastEvent,这个事件有两个子类型,TOAST1和TOAST2,其他的观察者可以按照自己需要,确定自己关心哪一个事件,这样,不仅解耦了Fragment和Activity,而且可以使得事件的观察者不是必须是Fragment,只要关心Activity的这个事件的类,都可以进行观察。下面看事件抛出的代码:

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) {
        R.id.show_toast1 ->
            EventBus.getDefault()
                    .post(ShowToastEvent(ShowToastEvent.EventType.TOAST1, "Toast1"))
        R.id.show_toast2 ->
            EventBus.getDefault()
                    .post(ShowToastEvent(ShowToastEvent.EventType.TOAST2, "Toast2"))
        else -> return super.onOptionsItemSelected(item)
    }

    return true
}

变化不大,就是修改了ShowToastEvent构造器的第一个参数。下面是FirstFragment的事件处理:

@Subscribe
fun onToastShow(event: MainActivity.ShowToastEvent) {
    if (event.type == MainActivity.ShowToastEvent.EventType.TOAST1) {
        Toast.makeText(context, "From First ${event.content}", Toast.LENGTH_SHORT).show()
    }
}

同样记得修改SecondFragment的代码。


现在的代码耦合很小,同时达到了目的。各位还有更好的方式,可以分享一下,大家一同学习。


阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yjp19871013/article/details/77816947
文章标签: Android EventBus
个人分类: Android
想对作者说点什么? 我来说一句

Android 关于EventBus使用

2018年03月21日 20.17MB 下载

android的数据传递EventBus使用解析

2017年02月24日 218KB 下载

没有更多推荐了,返回首页

不良信息举报

如何更好地使用EventBus

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭