Android EventBus3.0 深入了解
xin891120
8年前
<p>通过上一篇文章<a href="http://www.open-open.com/lib/view/open1469492199736.html">《EventBus 3.0相见恨晚》</a>对EventBus3.0的原理及使用方法有了简单了解。</p> <p>下面就其原理和使用方法做更深入细致的了解。</p> <h2>EventBus设计模式</h2> <p><em>EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling</em></p> <p>这句话是greenrobot官网对EventBus的解释。</p> <p>EventBus是一个针对Android为了松耦合的基于事件发布/订阅模式(<a href="http://www.open-open.com/lib/view/open1469492474661.html">观察者模式</a>)的开源库。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/63e5bc798e73c2e4d7a5383a8461ffb9.png"></p> <p style="text-align:center">框架</p> <p>从上图我们可以看到EventBus的设计结构非常简单。</p> <h2>EventBus 使用方式</h2> <h3>订阅者方法详细用法</h3> <p>从之前对EventBus的Demo我们可以看到,使用EventBus的关键是<strong>订阅者方法</strong>的实现,也就是事件处理方法onMessageEvent的实现。这个方法,才是我们处理数据、实现UI更新的关键。</p> <pre> <code> @Subscribe(threadMode = ThreadMode.POSTING,sticky = false,priority = 1) public void onMessageEvent(MessageEvent event) { tv.setText(event.message); }</code></pre> <p>这里再强调一遍,这个订阅者方法<strong>一定要添加@Subscribe这个注解</strong>,这个注解的完整参数如上,后面是一些可选的参数。下面就各个参数做一下分析:</p> <p>EventBus的ThreadMode总共有四种,并且都是在订阅者中的@Subscribe里进行制定的。下面就来看一下这四种ThreadMode。</p> <p>四种threadMode模式</p> <ul> <li><strong>ThreadMode: POSTING</strong><br> 这时候订阅者执行的线程与事件的发布者所在的线程为同一个线程。也就是说事件由哪个线程发布的,订阅者就在哪个线程中执行。这个也是EventBus默认的线程模式;由于没有线程的切换,也就意味消耗的资源也是最小的。如果一个任务不需要多线程的,也是推荐使用这种ThreadMode的。</li> </ul> <blockquote> <p>我们之前的Demo就是这样,事件发送是在SecondActivity的主线程,那么onMessageEvent在默认情况下必然会在主线程执行。</p> </blockquote> <ul> <li><strong>ThreadMode: MAIN</strong><br> 从它的名字就很容易可以看出,他是在Android的主线程中运行的。如果提交的线程也是主线程,那么他就和ThreadMode.POSTING一样了。当然在这里由于是在主线程中运行的,所以在这里就不能执行一些耗时的任务。</li> </ul> <blockquote> <p>还是之前的Demo,我们在SecondActivity中实现登录操作,正常情况下这必然是个网络请求,而这个网络请求必然不会在主线程(UI线程)中发生,所以,当我们完成网络请求发布事件的时候,发布事件所在的线程就不再是UI线程了,我们的onMessageEvent的ThreadMode就不能为默认值,必须指定为MAIN,确保其在主线程进行对UI的操作。</p> </blockquote> <ul> <li><strong>ThreadMode: BACKGROUND </strong><br> 这种模式下,我们的订阅者将会在后台线程中执行。如果发布者是在主线程中进行的事件发布,那么订阅者将会重新开启一个子线程运行,若是发布者在不是在主线程中进行的事件发布,那么这时候订阅者就在发布者所在的线程中执行任务。</li> </ul> <blockquote> <p>这种情况,一般是我们需要在<strong>订阅者方法</strong>中进行耗时的操作。</p> </blockquote> <ul> <li><strong>ThreadMode: ASYNC</strong><br> 在这种模式下,订阅者将会独立运行在一个线程中。不管发布者是在主线程还是在子线程中进行事件的发布,订阅者都是在重新开启一个线程来执行任务。</li> </ul> <p>这里我们可以简单的测试一下,加深理解:</p> <p>下面代码用<strong>4种不同</strong>的Thread模式订阅<strong>同一事件</strong>MessageEvent</p> <pre> <code>@Subscribe(threadMode = ThreadMode.PostThread) public void onMessageEventPostThread(MessageEvent messageEvent) { Log.e("PostThread", Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.MainThread) public void onMessageEventMainThread(MessageEvent messageEvent) { Log.e("MainThread", Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.BackgroundThread) public void onMessageEventBackgroundThread(MessageEvent messageEvent) { Log.e("BackgroundThread", Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.Async) public void onMessageEventAsync(MessageEvent messageEvent) { Log.e("Async", Thread.currentThread().getName()); }</code></pre> <ul> <li>在主线程发布事件:</li> </ul> <pre> <code>Log.e("postEvent", Thread.currentThread().getName()); EventBus.getDefault().post(new MessageEvent());</code></pre> <p>看一下日志输出:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4d604fde3014eee454aad54f7878dc04.png"></p> <p style="text-align:center">主线程中发布事件</p> <blockquote> <p>可以看出,在主线程发布事件,那么Post和Main 模式下,订阅者方法也是在主线程执行,其他两种模式下分别是独立的线程。</p> </blockquote> <ul> <li>在子线程发布事件 <pre> <code>new Thread(new Runnable() { @Override public void run() { Log.e("postEvent", Thread.currentThread().getName()); EventBus.getDefault().post(new MessageEvent()); } }).start();</code></pre> 看一下日志输出:</li> </ul> <p style="text-align:center"><img src="https://simg.open-open.com/show/e5b18d8972e4a98a523709d4b2c15240.png"></p> <p style="text-align:center">子线程发布事件</p> <blockquote> <p>子线程发布事件时,只有ThreadMode 为MAIN时,订阅者方法才会在主线程执行。其余都是在独立的线程。</p> </blockquote> <p>sticky的意义</p> <p>这个单词sticky的意思是粘贴性,这里的意思就是<strong>在发送事件之后再订阅该事件也能收到该事件</strong><br> 默认为false。</p> <p>priority 的意义</p> <p>这个priority的意义是优先级,这里主要说一下结论和注意事项</p> <ul> <li>优先级高(priority值大)的将会首先接收到发布者所发布的事件</li> <li>优先级只是针对于相同的ThreadMode中</li> <li>默认的优先级为0</li> </ul> <p>平时使用时,可以根据实际需求做调整。</p> <h3>订阅者索引相关</h3> <p>在性能方面,EventBus 3由于使用了注解,比起使用反射来遍历方法的2.4版本逊色不少。但开启索引后性能远远超出之前的版本。下面就来看一下对于EventBus的另一种使用方式。</p> <p>在Project的build.gradle中添加如下代码:</p> <pre> <code>buildscript { dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } }</code></pre> <p>然后在app的build.gradle中添加如下代码:</p> <pre> <code>apply plugin: 'com.neenbedankt.android-apt' dependencies { compile 'org.greenrobot:eventbus:3.0.0' apt 'org.greenrobot:eventbus-annotation-processor:3.0.1' } apt { arguments { eventBusIndex "com.example.myapp.MyEventBusIndex" } }</code></pre> <p>重新rebuild之后会在build目录下面生成MyEventBusIndex文件,文件名可以自定义。下面就来看一下如何使用这个MyEventBusIndex.我们可以自定义设置自己的EventBus来为其添加MyEventBusIndex对象。代码如下所示:</p> <pre> <code>EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();</code></pre> <p>我们也能够将MyEventBusIndex对象安装在默认的EventBus对象当中。代码如下所示:</p> <pre> <code>EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); // Now the default instance uses the given index. Use it like this: EventBus eventBus = EventBus.getDefault();</code></pre> <p>剩下对于EventBus的用法则是一模一样。当然也建议通过添加订阅者索引这种方式来使用EventBus,这样会比通过反射的方式来解析注解效率更高。</p> <h2>关于EventBus的思考</h2> <p>不使用EventBus的前提下,在Android内部各个组件的交互通信可以说是杂乱无章,各种接口定义,注册、回调。搞得整个代码有着很严重的耦合,接口中多一个参数,参数类型变化都会导致很多地方要改。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/8715304caadfa706aac9873fe339f307.png"></p> <p style="text-align:center">传统的组件交互</p> <p>这种思路注定了随着项目越来越庞大,耦合会越发的严重,所以势必需要一种新的思路来解决这个问题。</p> <p>而EventBus的出现,很好的解决了这个问题</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a9a82c991ae97355c6fed3eac7bda5ce.png"></p> <p style="text-align:center">EventBus</p> <p>从这两幅图,我们可以清晰的感受到,EventBus给我们代码整体的结构带来多么大的益处,单从图上就可以看到明显减少了各个组件之间的耦合;同时从代码角度出发,EventBus短短几行代码,就可以实现之前通过接口-注册接口-回调 等一系列的工作才能完成的工作量。</p> <p>当然,凡事要好必然有坏,EventBus使用方式的简单,也会导致我们在出现问题时无从下手,这就需要长时间的积累经验了。但是总的来说,还是利大于弊吧,所以当我们项目有大量的事件交互时,EventBus不失为一种好的选择!</p> <p><br> 来自:http://www.jianshu.com/p/dbdb92f6f0e2</p> <p> </p>