EventBus使用、案例、代码混淆报错

前言:当我们进行项目开发的时候,往往是需要应用程序的各组件,组件与后台线程间进行通信,比如在子线程中进行请求数据,当数据请求完毕后通过处理程序或者是广播通知UI,而两个片段之家可以通过监听器进行通信等等。当我们的项目越来越复杂,使用意向,处理程序,广播进行模块间通信,模块与后台线程进行通信时,代码量大,而且高度耦合。现在就让我们来学习一下EventBus 3.0吧。

首先使用EventBus之前,我们先要了解一下EventBus:

  • 简化组件之间的通信
    解耦事件发送器和接收器
    与活动,片段和后台线程良好地运行,
    避免了复杂且容易出错的依赖关系和生命周期问题
  • 使您的代码更简单
  • 很快
  • 很小(约50k罐)
  • 已经通过100,000,000+安装的应用程序在实践中得到证实
  • 具有交付线程,用户优先级等高级功能。

 

  1. Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。
  2. Subscriber:事件订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEventonEventMainThreadonEventBackgroundThreadonEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING
  3. Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。

1.2 四种线程模型

EventBus3.0有四种线程模型,分别是:

  1. POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
  2. MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
  3. BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
  4. ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

2、EventBus 使用

2.1 引入依赖

在使用之前先要引入如下依赖:

implementation 'org.greenrobot:eventbus:3.1.1'

2.2 定义事件

然后,我们定义一个事件的封装对象。在程序内部就使用该对象作为通信的信息:

public class MessageWrap {

    public final String message;

    public static MessageWrap getInstance(String message) {
        return new MessageWrap(message);
    }

    private MessageWrap(String message) {
        this.message = message;
    }
}

2.3 发布事件

然后,我们定义一个Activity:

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY1)
public class EventBusActivity1 extends CommonActivity<ActivityEventBus1Binding> {

    @Override
    protected void doCreateView(Bundle savedInstanceState) {
        // 为按钮添加添加单击事件
        getBinding().btnReg.setOnClickListener(v -> EventBus.getDefault().register(this));
        getBinding().btnNav2.setOnClickListener( v ->
                ARouter.getInstance()
                        .build(BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
                        .navigation());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onGetMessage(MessageWrap message) {
        getBinding().tvMessage.setText(message.message);
    }
}

这里我们当按下按钮的时候向EventBus注册监听,然后按下另一个按钮的时候跳转到拎一个Activity,并在另一个Activity发布我们输入的事件。在上面的Activity中,我们会添加一个监听的方法,即onGetMessage,这里我们需要为其加入注解Subscribe并指定线程模型为主线程MAIN。最后,就是在Activity的onDestroy方法中取消注册该Activity。

下面是另一个Activity的定义,在这个Activity中,我们当按下按钮的时候从EditText中取出内容并进行发布,然后我们退出到之前的Activity,以测试是否正确监听到发布的内容。

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
public class EventBusActivity2 extends CommonActivity<ActivityEventBus2Binding> {

    @Override
    protected void doCreateView(Bundle savedInstanceState) {
        getBinding().btnPublish.setOnClickListener(v -> publishContent());
    }

    private void publishContent() {
        String msg = getBinding().etMessage.getText().toString();
        EventBus.getDefault().post(MessageWrap.getInstance(msg));
        ToastUtils.makeToast("Published : " + msg);
    }
}

根据测试的结果,我们的确成功地接收到了发送的信息。

2.4 黏性事件

所谓的黏性事件,就是指发送了该事件之后再订阅者依然能够接收到的事件。使用黏性事件的时候有两个地方需要做些修改。一个是订阅事件的地方,这里我们在先打开的Activity中注册监听黏性事件:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGetStickyEvent(MessageWrap message) {
    String txt = "Sticky event: " + message.message;
    getBinding().tvStickyMessage.setText(txt);
}

另一个是发布事件的地方,这里我们在新的开的Activity中发布黏性事件。即调用EventBus的postSticky方法来发布事件:

private void publishStickyontent() {
    String msg = getBinding().etMessage.getText().toString();
    EventBus.getDefault().postSticky(MessageWrap.getInstance(msg));
    ToastUtils.makeToast("Published : " + msg);
}

按照上面的模式,我们先在第一个Activity中打开第二个Activity,然后在第二个Activity中发布黏性事件,并回到第一个Activity注册EventBus。根据测试结果,当按下注册按钮的时候,会立即触发上面的订阅方法从而获取到了黏性事件。

2.5 优先级

Subscribe注解中总共有3个参数,上面我们用到了其中的两个,这里我们使用以下第三个参数,即priority。它用来指定订阅方法的优先级,是一个整数类型的值,默认是0,值越大表示优先级越大。在某个事件被发布出来的时候,优先级较高的订阅方法会首先接受到事件。

为了对优先级进行测试,这里我们需要对上面的代码进行一些修改。这里,我们使用一个布尔类型的变量来判断是否应该取消事件的分发。我们在一个较高优先级的方法中通过该布尔值进行判断,如果未true就停止该事件的继续分发,从而通过低优先级的订阅方法无法获取到事件来证明优先级较高的订阅方法率先获取到了事件。

这里有几个地方需要注意

  1. 只有当两个订阅方法使用相同的ThreadMode参数的时候,它们的优先级才会与priority指定的值一致;
  2. 只有当某个订阅方法的ThreadMode参数为POSTING的时候,它才能停止该事件的继续分发。

所以,根据以上的内容,我们需要对代码做如下的调整:

// 用来判断是否需要停止事件的继续分发
private boolean stopDelivery = false;

@Override
protected void doCreateView(Bundle savedInstanceState) {
    // ...

    getBinding().btnStop.setOnClickListener(v -> stopDelivery = true);
}

@Subscribe(threadMode = ThreadMode.POSTING, priority = 0)
public void onGetMessage(MessageWrap message) {
    getBinding().tvMessage.setText(message.message);
}

// 订阅方法,需要与上面的方法的threadMode一致,并且优先级略高
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = 1)
public void onGetStickyEvent(MessageWrap message) {
    String txt = "Sticky event: " + message.message;
    getBinding().tvStickyMessage.setText(txt);
    if (stopDelivery) {
        // 终止事件的继续分发
        EventBus.getDefault().cancelEventDelivery(message);
    }
}

即我们在之前的代码之上增加了一个按钮,用来将stopDelivery的值置为true。该字段随后将会被用来判断是否要终止事件的继续分发,因为我们需要在代码中停止事件的继续分发,所以,我们需要将上面的两个订阅方法的threadMode的值都置为ThreadMode.POSTING

按照,上面的测试方式,首先我们在当前的Activity注册监听,然后跳转到另一个Activity,发布事件并返回。第一次的时候,这里的两个订阅方法都会被触发。然后,我们按下停止分发的按钮,并再次执行上面的逻辑,此时只有优先级较高的方法获取到了事件并将该事件终止。


简单走一遍流程:

首先新建一个实体类:

/**
* @desc 这个类就是一个Bean的类,里面定义用来传输的数据的类型。
* @author lijianguo
* @date 2019/3/8
* @version
*/
public class MessageEvent {

    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

 

OneActivity:

public class MainActivity extends AppCompatActivity  {

    private TextView tvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在活动的的onCreate()方法里去注册EventBus,在onDestory()方法里,去解除注册。
        tvText = (TextView) findViewById(R.id.tv_text);
        tvText.setText("你好");
        tvText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this,MyTwoActivity.class);
                startActivity(intent);
            }
        });
        EventBus.getDefault().register(this);
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void initData(MessageEvent messageEvent) {
        tvText.setText(messageEvent.getMessage());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

 

TwoActivity:

public class MyTwoActivity extends AppCompatActivity {

    private TextView tvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvText = (TextView) findViewById(R.id.tv_text);
        tvText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new MessageEvent("你来了"));
                finish();
            }
        });

    }
}

案例1:

在A界面想B界面传递消息

一般情况下使用intent直接传递就可以了,但是如果不存在跳转,就可以使用粘性事件发送消息:

EventBusUtils 类:封装发送方法以及注册方法

public class EventBusUtils extends AppCompatActivity {

    public EventBusUtils() {
    }

    /**
     * 注册 EventBus
     * @param subscriber
     */
    public static void register(Object subscriber) {
        EventBus eventBus = EventBus.getDefault();
        if (!eventBus.isRegistered(subscriber)) {
            eventBus.register(subscriber);
        }}

    /**
     * 解除注册 EventBus
     * @param subscriber
     */
    public static void unregister(Object subscriber) {
        EventBus eventBus = EventBus.getDefault();
        if (eventBus.isRegistered(subscriber)) {
            eventBus.unregister(subscriber);
        }}

    /**
     * 发送事件消息
     *
     * @param event
     */
    public static void post(MessageEvent event) {
        EventBus.getDefault().post(event);
    }

    /**
     * 发送粘性事件消息
     *
     * @param event
     */
    public static void postSticky(MessageEvent event) {
        EventBus.getDefault().postSticky(event);
    }

}

---------------------------------------------------------------------

MessageEvent:是消息Bean类,传递消息类型

public class MessageEvent<T> {

    //消息内容
    private T message;
    //消息类型标识
    private int code;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public MessageEvent(int code, T message) {
        this.message = message;
        this.code = code;
    }

    public T getMessage() {
        return message;
    }

    public void setMessage(T message) {
        this.message = message;
    }
}

---------------------------------------------------------------------

MainActivity :A界面,向B界面进行传递,这里跳转是为了展示B数据,通过postSticky发送粘性事件,

public class MainActivity extends EventBusUtils {
    @BindView(R.id.bt_bus)
    Button btBus;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        //第一个是要传递的标记   第二个参数是数据
        postSticky(new MessageEvent<>(1000, "EventData"));

        btBus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, TwoActivity.class);
                startActivity(intent);
            }
        });
    }
}

---------------------------------------------------------------------

TwoActivity :B界面,用来接收A界面发送的消息,通过 @Subscribe订阅者来订阅该方法,并且进行注册与解注册。

package com.example.ljg.myeventbus;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import butterknife.BindView;
import butterknife.ButterKnife;

public class TwoActivity extends EventBusUtils {

    @BindView(R.id.tv_text)
    TextView tvText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_two);
        ButterKnife.bind(this);
        register(this);
    }
    //标记和发送消息的标记一样的,包括类型和值都必须一样
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onEvent(MessageEvent event) {
        Toast.makeText(this, "" + event.getMessage(), Toast.LENGTH_SHORT).show();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregister(this);
    }

}

案例结束。

提示:当A直接发送B界面时,如果B界面没有注册,是收不到消息的。即使发布者发了消息,但订阅者还未产生(一般消息的处理逻辑是先注册订阅,后接收),这样没有收到消息当然无法响应操作,所以就需要用到粘性广播。如果在发送之前,接受界面已经注册,那么就可以直接使用Post来直接发送。这样就可以更好的理解EventBus。走过的坑,记录~

 

案例2:

案例情况:在A界面已经注册,在B界面通过Post发送,在A界面进行接收。

MainActivity :A界面已经启动,在B界面发送后在A界面接收

public class MainActivity extends EventBusUtils {
    @BindView(R.id.bt_bus)
    Button btBus;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        register(this);
        btBus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, TwoActivity.class);
                startActivity(intent);
            }
        });
    }
    //标记和发送消息的标记一样的,包括类型和值都必须一样
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onEvent(MessageEvent event) {
        Toast.makeText(this, "" + event.getMessage(), Toast.LENGTH_SHORT).show();
        Log.e("TAG", "event=" + event.getMessage());
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregister(this);
    }
}

 

------------------------------------------------------

TwoActivity :B界面发送

public class TwoActivity extends EventBusUtils {

    @BindView(R.id.tv_text)
    TextView tvText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_two);
        ButterKnife.bind(this);
        //第一个是要传递的标记   第二个参数是数据
        post(new MessageEvent<>(1000, "EventData"));
    }
}

 

EventBus踩坑:

在Demo中是没有问题的,加到项目就报错了,加入官方文档的混淆代码就可以了。报错信息:

报错代码:

EventBusUtils.registerEvent(this);//注册时候报错
java.lang.NoSuchFieldError: No static field MAIN of type Lorg/greenrobot/eventbus/ThreadMode; in class Lorg/greenrobot/eventbus/ThreadMode; or its superclasses (declaration of 'org.greenrobot.eventbus.ThreadMode' appears in /data/app/cn.com.ngds.gamestore.oem-1/base.apk)
                                                       at java.lang.reflect.Method.getAnnotationNative(Native Method)
                                                       at java.lang.reflect.Method.getAnnotation(Method.java:557)

变量找不到,很明显,又是被ProGuard给去掉了。于是在progrard-rules.pro加一句:

加到:proguard-rules文件中


-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# EventBus 3.0
-keepclassmembers class ** {
    public void onEvent*(**);
}

# EventBus 3.0 annotation
-keepclassmembers class * {
    @de.greenrobot.event.Subscribe <methods>;
}

添加完后重新编译一下项目,就可以了。
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@删库跑路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值