一、什么是EventBus
前言
当我们进行项目开发的时候,往往是需要应用程序的各组件、组件与后台线程间进行通信,比如在子线程中进行请求数据,当数据请求完毕后通过Handler或者是广播通知UI,而两个Fragment之家可以通过Listener进行通信等等。当我们的项目越来越复杂,使用Intent、Handler、Broadcast进行模块间通信、模块与后台线程进行通信时,代码量大,而且高度耦合。现在就让我们来学习一下EventBus 3.0吧。
EventBus
EventBus能够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。
二、特点优势
simplifies the communication between components
decouples event senders and receivers
performs well with Activities, Fragments, and background threads
avoids complex and error-prone dependencies and life cycle issues
makes your code simpler
is fast
is tiny (~50k jar)
is proven in practice by apps with 100,000,000+ installs
has advanced features like delivery threads, subscriber priorities, etc.
三、步骤
举个例子,我需要在一个Activity里注册EventBus事件,然后定义接收方法,这跟Android里的广播机制很像,你需要首先注册广播,然后需要编写内部类,实现接收广播,然后操作UI。所以,在EventBus中,你同样得这么做。
fragment代码
package com.example.day0012;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import org.greenrobot.eventbus.EventBus;
/**
* A simple {@link Fragment} subclass.
*/
public class BlankFragment extends Fragment {
private Button fgBtn;
public BlankFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View inflate = inflater.inflate(R.layout.fragment_blank, null);
fgBtn = (Button) inflate.findViewById(R.id.fg_btn);
fgBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//发送
String s = "123";
EventBus.getDefault().post(s);
}
});
return inflate;
}
}
fragment xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BlankFragment">
<!-- TODO: Update blank fragment layout -->
<Button
android:layout_width="wrap_content"
android:text="send"
android:id="@+id/fg_btn"
android:layout_height="wrap_content"></Button>
</LinearLayout>
MainActivity代码
package com.example.day0012;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
}
@Subscribe
public void getString(String s){
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
MainActivity xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/fg"
android:name="com.example.day0012.BlankFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"></fragment>
</LinearLayout>
四、三要素
Event 事件。它可以是任意类型。
Subscriber 事件订阅者。在EventBus3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且指定线程模型,默认是POSTING。
Publisher 事件的发布者。我们可以在任意线程里发布事件,一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
五、线程模型
POSTING
POSTING是默认线程模式,在哪个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。因此,对于不要求是主线程并且耗时很短的简单任务推荐使用该模式。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MAIN
订阅者方法将在主线程(UI线程)中被调用。因此,可以在该模式的订阅者方法中直接更新UI界面。事件处理的时间不能太长,长了会导致ANR。
MAIN_ORDERED
订阅者在主线程中调用。该事件总是排队等待以后交付给订阅者,因此对post的调用将立即返回。这为事件处理提供了更严格且更一致的顺序,例如,在具有MAIN线程模式的事件处理程序中发布另一个事件,则第二个事件处理程序将在第一个事件处理程序之前完成,事件处理的时间不能太长,长了会导致ANR。
BACKGROUND
订阅者将在后台线程中调用。如果发布线程不是主线程,则将在发布线程中直接调用事件处理程序方法。如果发布线程是主线程,则EventBus使用单个后台线程,该线程将按顺序传递其所有事件。在此事件处理函数中禁止进行UI更新操作。
ASYNC
无论事件在哪个线程中发布,该事件处理函数都会在新建的子线程中执行;同样,此事件处理函数中禁止进行UI更新操作。
EventBus3.0有四种线程模型,分别是:
POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
六、粘性事件
所谓粘性事件,就是在发送事件之后再订阅该事件也能收到该事件。请注意这里与普通事件的区别,普通事件是先注册在绑定。
普通事件:通过post()方法发出的普通事件,会被已经注册的订阅者接收到,若订阅者是在消息发送之后才注册,那么是不会接收到该事件的
粘性事件:而粘性事件是可以被事件发出之后才注册的订阅者接收到,也可以在事件发出之后通过主动查询获取事件内容。粘性事件实现原理其实是把最近的事件缓存到内存中,之后注册的订阅者还可以查询出来
比如在AActivity中发送一个粘性事件Event,然后打开BActivity,在BActivity中注册Event的粘性事件订阅者,在注册后马上可以接收到该事件。而如果是普通事件的话是接收不到的,因为订阅者是在消息发送之后才注册的。这就是粘性事件的方便之处。
代码
1.第一个Activity xml代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转"></Button>
</LinearLayout>
activity代码
package com.example.eventbus;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
public class MainActivity extends AppCompatActivity {
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
EventBus.getDefault().postSticky("哈哈哈");
}
private void initView() {
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivity(intent);
}
});
}
}
1.第二个Activity代码
package com.example.eventbus;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
EventBus.getDefault().register(this);
}
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void getEventBus(String s){
Toast.makeText(this, ""+s, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}