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"?>        

菜单的创建和处理:

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()}@Subscribefun 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,                          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}

最后是订阅的代码:

@Subscribefun 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的事件处理:

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

同样记得修改SecondFragment的代码。


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


更多相关文章

  1. Android(安卓)4.0 input touch解析(一)
  2. android 数据储存——--文件存储(2)
  3. 超简单的几行代码搞定Android底部导航栏功能
  4. Android中禁用屏幕旋转
  5. Android(安卓)判断系统用户无操作
  6. 深入浅出Android事件分发机制——源码分析篇
  7. Android调用摄像头和本地相册
  8. Run c++ program with boost on Android
  9. Android(安卓)PopupWindow 实现自定义弹出层

随机推荐

  1. Android架构组件- Room数据库的使用
  2. Android(安卓)invalidate与postInvalidat
  3. 使用myelipse配置android开发环境
  4. Android开发教程
  5. Android相机Camera基础知识
  6. AndroidResource
  7. GridView控件的简单使用
  8. [android]Android异步处理系列文章索引
  9. Android 图表开源框架之MPAndroidChart L
  10. [Android] Android之Service案例-电话窃