点亮技能之EventBus框架的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pihailailou/article/details/81836656

目录

一,写在前面

二,事件类型

三,订阅事件

四,订阅方法

五,发送事件

六,粘性事件的特点

七,注销事件

八,最后


一,写在前面

       本篇文章将会介绍EventBus 3.0版本的使用,值得一提的是,阅读本片文章前需要了解Java注解相关的知识点,可参考文章Java注解。EventBus是一个事件发布/订阅轻量级框架,用于在Activity,Fragment,Service的各个线程间传递数据。使用EvenBus开发者只需要关注:事件的发送,事件的订阅/注销,事件类型的定义,and提供订阅方法,一会咱们就能体会到EventBus在数据传递上带来的方便。

本篇文章包括如下内容:

  • 事件类型
  • 订阅(注册)事件
  • 订阅方法
  • 发送事件
  • 粘性事件的特点
  • 注销事件

二,事件类型

前面我们提到EventBus是在各个模块间传递数据,那么数据的载体是什么呢?基于面向对象的思想,答案自然是实体类啦。因此在使用EventBus框架前,需要自定义一个实体类,于是创建一个EventMessage的类。

代码如下:

public class EventMessage {
    public String message;
    public EventMessage(String message) {
        this.message = message;
    }
}

在EventMessage类中,定义了一个String类型的变量,一会咱们的数据就以字符串的形式传递。


三,订阅事件

下面以在Fragment中订阅事件为例进行展示,关于Fragment布局相关的代码就不粘贴了。

核心代码如下:

    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag1, container, false);
        tv1 = view.findViewById(R.id.tv1);

        Button button = view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().register(EventbusFragment1.this);
            }
        });
        return view;
    }

9行:EventBus.getDefault()返回一个框架提供的默认的EventBus对象;EventBus$register方法中传入的参数是一个订阅类的实例,这里是一个Fragment对象,系统会遍历这个Fragment中的订阅方法。因此要注意这里参数不能传this,这里this指的是OnClickListener实例。


四,订阅方法

订阅方法要在订阅类EventBusFragment1里定义,直接上代码:

    @Subscribe(threadMode = ThreadMode.MAIN, priority = 10, sticky = true)
    public void onEvent(EventMessage em) {
        tv1.setText(em.message);
        Log.e("wcc",em.message + "...onEvent");
    }

2行:方法名是onEvent,3.0后订阅方法的名称可自定义。权限修饰符是public,且不能被static,abstract修饰。方法参数是事件类型EventMessage。

1行:由于方法名可以自定义,要想让系统在遍历EventBusFragment1类的方法时,能认识谁才是订阅方法,那么需要在订阅方法上面添加注解@Subscribe。该注解由三个属性值,分别是threadMode(),priority(),sticky()。

@Subscribe定义如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

threadMode属性:订阅方法所在的线程,有四种值分别是:POSTING,MAIN,BACKGROUND,ASYNC,默认值是POSTING。

  • POSTING:订阅方法和发送事件在同一个线程里;
  • MAIN:订阅方法在主线程中执行;
  • BACKGROUND:若发送事件在子线程中执行,订阅方法跟发送事件在一个线程里;若发送事件在主线程中执行,系统会使用线程池开启一个新的子线程,去执行订阅方法;
  • ASYNC:系统使用线程池开启一个新的子线程,去执行订阅方法;

sticky属性:可以接受粘性事件,当然也可以接受普通事件,粘性事件在后面会具体介绍。

priority属性:决定订阅方法接受事件的顺序,优先级越高,越早收到事件;


五,发送事件

我们在EventbusFragment1中订阅了事件,并提供了订阅方法onEvent。下面在EventbusFragment2中发送事件,其布局文件代码就不提供了。

核心代码如下:

    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag2, container, false);
        Button button = view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               EventBus.getDefault().post(new EventMessage("数据来自fragment2...post1"));
            }
        });
        return view;
    }

调用EventBus的post方法,方法参数是事件类型EventMessage的实例。

MainActivity页面效果图如下:

先点击按钮"订阅事件",再点击按钮"发送事件",效果图如下:

可以发现:fragment2将数据传递到了fragment1中,fragment1中的TextView的文字来自于fragment2。

如果不使用EventBus框架,首先需要自定义接口,在fragment2中回调接口的方法,在fragment1中注册接口并重写回调方法。使用EventBus框架解耦了模块fragment1和fragment2,使用起来也非常简单。


六,粘性事件的特点

第五点讲到发送事件,调用了post方法,该方法发送的是一个普通事件。普通事件只能先订阅,后发送,否则订阅方法不会接受到事件。如果希望事件先发送后订阅,订阅方法仍可以接受到事件,那么就需要发送粘性事件,调用postSticky方法。

在EventbusFragment2中发送两个粘性事件(事件类型相同),代码如下:

EventBus.getDefault().postSticky(new EventMessage("数据来自fragment2...postSticky1"));
EventBus.getDefault().postSticky(new EventMessage("数据来自fragment2...postSticky2"));

在EventbusFragment1中提供一个订阅方法接受粘性事件,代码如下:

    @Subscribe(threadMode = ThreadMode.MAIN, priority = 10, sticky = true)
    public void onEvent(EventMessage em) {
        Log.e("wcc",em.message + "...onEvent");
    }

先点击按钮"订阅事件",再点击按钮"发送事件",打印log如下:

08-18 19:22:37.277 13666-13666/wcc.org.eventbus E/wcc: 数据来自fragment2...postSticky1...onEvent
08-18 19:22:37.277 13666-13666/wcc.org.eventbus E/wcc: 数据来自fragment2...postSticky2...onEvent

先点击按钮"发送事件",再点击按钮"订阅事件",打印log如下:

08-18 19:25:42.931 13783-13783/wcc.org.eventbus E/wcc: 数据来自fragment2...postSticky2...onEvent

我们发现:

  1. 订阅事件,后发送事件,粘性事件与普通事件效果一样。发送多少个事件,就接受多少个事件,订阅方法就调用多少次。
  2. 发送事件,后订阅事件,普通事件的订阅方法无法接受到事件,粘性事件的订阅方法只接受到最近一次发送的事件。

为什么粘性事件在先发送,后订阅的情况下,订阅方法可以被回调呢?为啥只接受最后一次的粘性事件呢?

查阅下postSticky方法的源码:

    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

3行:stickyEvents是一个ConcurrentHashMap集合,key:事件类型的class实例,value:事件类型的实例。在调用register方法注册事件时,订阅方法的@Subscribe注解的sticky属性值为true时,它会取出stickyEvents中的数据去回调订阅方法。也就是说,postSticky比post方法多了一个缓存事件类型数据的操作,方便订阅事件时取出来。

又由于HashMap存放的数据不可重复,因此调用多次postSticky时,只有最后一次的数据被存储在Map集合中。因此,对于同一事件类型,只有最后一个粘性事件被接受。

6行:调用post方法;

为什么粘性事件在先订阅,后发送的情况下,效果跟普通事件一样呢?

由上面分析可知:先订阅事件,postSticky方法仍未执行,stickyEvents集合中没有数据,拿不到缓存中的数据。此时粘性事件的代码逻辑和普通事件基本相同,由于本文定位不是源码解析EventBus,故不做过多阐述。后面会写一篇EventBus的实现原理的文章,敬请期待~


七,注销事件

当订阅类不需要接受发送过来的事件时,可以在订阅类中注销事件,同时还可以避免内存泄漏。Activity一般在onDestroy中注销事件,普通事件的注销,代码如下:

EventBus.getDefault().unregister(this); //this表示Activity的实例

粘性事件的注销,代码如下:

EventBus.getDefault().unregister(this); //this表示Activity的实例
EventBus.getDefault().removeAllStickyEvents(); //移除所有的粘性事件,代表缓存的stickyEvents集合清空

如果只想移除某一个事件类型的粘性事件,则调用EventBus的如下方法:

public <T> T removeStickyEvent(Class<T> eventType) //参数是事件类型的class实例

public boolean removeStickyEvent(Object event) //参数是事件类型的实例

八,最后

涉及到Activity,Service,Fragment模块间数据的传递,不要忘了使用轻量级框架EventBus,它替换了使用传统的intent,Handler+Message,Broadcast,接口回调等方式来传递数据。即便需要满足在不同线程中执行,EventBus也可以很好的工作,而无需开发者再写一堆Handler+Message相关的代码实现线程间的通信。

需要注意的是,EventBus并不支持IPC通信,进程间通信的手段使用传统的方式更simple。

好啦,关于EventBus的基本使用,到这里基本结束~

                                                                                                                       O(∩_∩)O

猜你喜欢

转载自blog.csdn.net/pihailailou/article/details/81836656