学习步骤
- EventBus简介
- EventBus方法介绍
- EventBus的实际应用
- 总结
以下来自:EventBus主页
开源项目地址:
https://github.com/greenrobot/EventBus
EventBus主页:
http://greenrobot.org/eventbus/
简介:
Introduction
EventBus is an open-source library for Android usingthe publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
EventBus是一个Android和Java的开源库,使用发布者/订阅者模式进行松散耦合。EventBus只需几行代码即可实现与分离类的集中通信 - 简化代码,消除依赖关系,并加速应用程序开发。
综合版:
EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。比如请求网络,等网络返回时通过Handler或Broadcast通知UI,两个Fragment之间需要通过Listener通信,这些需求都可以通过EventBus实现。
使用EventBus的好处:它......
进一步的EventBus功能
方便的基于注释的API:基于便捷注释的API:只需将@Subscribe注释放入订阅者方法即可。由于注释的构建时间索引,EventBus不需要在应用程序的运行时进行注释反射。
- Android主线程交付:当与UI交互时,无论事件如何发布,EventBus都可以在主线程中传递事件。
- 后台线程传递:如果您的订阅者执行长时间运行的任务,EventBus也可以使用后台线程来避免UI阻塞。
- 事件和订阅者继承:在EventBus中,面向对象的范例适用于事件和订阅者类。假设事件类A是B的超类。类型B的已发布事件也将发布给对A感兴趣的订阅者。同样考虑订阅者类的继承。
- 快速入门:您可以立即开始使用 - 无需配置任何内容 - 使用代码中任何位置提供的默认EventBus实例。
- 可配置: 要根据您的要求调整EventBus,您可以使用构建器模式调整其行为。
开始使用EventBus
有关EventBus的第一步,请查看文档/教程,尤其是入门指南。
作为一个消息总线,有三个主要的元素:
• Event:事件
• Subscriber:事件订阅者,接收特定的事件
• Publisher:事件发布者,用于通知Subscriber有事件发生
Event 可以是任意对象,用来描述传递的数据和事件类型。
可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法 ,可以自己实例化EventBus对象,但一般使用默认的单例就好了: EventBus.getDefault(),根据post函数参数的类型,会自动调用订阅相应类型事件的函数。
在EventBus中,使用约定来制定事件订阅者以简化使用。
3.0之 前Subscriber函数的名字只能是以下4个
- onEvent
- onEventMainThread
- onEventBackgroundThread
- onEventSync
这四个,这个和ThreadMode有关。
ThreadMode制定了会调用的函数
3.0之 前有以下四个ThreadMode:
@Subscribe(threadMode = ThreadMode.ASYNC)
- PostThread(发送和接收都在同一个线程内)
- MainThread(发送和接收都是在主线程)
- BackgroundThread (同步的,接收是在单独的线程内,如果同时执行多个 依次执行)
- Async (异步的处理耗时操作,接收是异步的,独占一个线程池的线程来处理)
3.0之后加上@Subscriber后,函数的名字不固定。但需要指明ThreadMode。
有以下四个ThreadMode:
- POSTING(默认的)
- MAIN
- BACKGROUND
- ASYNC
- - 事件的处理在和事件的发送在相同的进程,所以事件的处理时间不应太长, 不然影响事件的发送线程,而这个线程可能是UI线程。
- - 对应的函数名是onEvent
简单案例:两个Activity间传递数据
布局文件省略
添加依赖
implementation 'org.greenrobot:eventbus:3.1.1'
第一个Activity代码:
public class MainActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.id_btn_main);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//跳转到第二个Activity中
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
//注册 必须有@Subscriber 订阅者
EventBus.getDefault().register(this);
}
@Subscribe
public void onEvent(MyEvent event) {
Toast.makeText(this, "我是第一个Activity,收到Event:"+event.msg, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销
EventBus.getDefault().unregister(this);
}
}
第二个Activity代码:
public class SecondActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mButton = findViewById(R.id.id_btn_second);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyEvent event = new MyEvent();
event.msg = " hello !";
//直接post不需要注册
EventBus.getDefault().post(event);
}
});
}
}
事件的处理会在一个后台线程中执行,对应的函数名是 onEventBackgroundThread,虽然名字是BackgroundThread,事件处理是在后台线程,但事件处理时间还是不应该太长,因为如果发送事件的线程是后台线程,会直接执行事件,如果当前线程是UI线程,事件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。
事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作 ,每个事件会开启一个线程(有线程池),但最好限制线程的数目。
在MainAcitivity中有两个fragment
一个HistoryFragment显示经纬度的listview
一个MapFragment显示通过百度API通过经纬度下载的静态图片的Image
当点击MOVELOCATION时发送一个随机经纬度
- HistoryFragment 订阅者:接收到经纬度的数据,更新adapter
- MapFragment 订阅者: 也接受到经纬度通过aysncTask在doInBackground中使用百度的API下载
- onPostExcute 中 返回下载好的drawable图像 此时post给自己(本身的fragment)显示图片
注意添加网络权限
注意:android 9.0 使用这个百度API 会报错
解决方法: android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法
完整代码:
模块的build.gradle中添加otto框架的依赖
implementation 'org.greenrobot:eventbus:3.1.1'
布局文件:
activity_main.xml
这里的fragment是静态添加的 注意修改为自己的包名
class="包名.LocationMapFragment"
Fragment:
LocationHistroyFragment.java
显示历史经纬度的Fragment
import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import com.squareup.otto.Subscribe;
import java.util.ArrayList;
import java.util.List;
/**
* 显示历史坐标的fragment
*/
public class LocationHistoryFragment extends ListFragment {
private final List locationEvents = new ArrayList<>();
private ArrayAdapter adapter;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
adapter = new ArrayAdapter(getActivity(),
android.R.layout.simple_list_item_1, locationEvents);
setListAdapter(adapter);
}
@Override
public void onResume() {
super.onResume();
//注册Bus
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
//注销Bus
EventBus.getDefault().unregister(this);
}
/**
* 订阅 坐标移动的事件 更新 listview
*
* @param event
*/
@Subscribe
public void onLocationMoveEvent(LocationMoveEvent event) {
float lng = event.longitude;
float lat = event.latitude;
locationEvents.add(String.format("[%s, %s]", lng, lat));
adapter.notifyDataSetChanged();
}
/**
* 订阅 坐标 清除的事件 清除list数据
*LocationClearEvent 暂时用不上 所以为空类
* @param event
*/
@Subscribe
public void onLocationClearEvent(LocationClearEvent event) {
locationEvents.clear();
adapter.notifyDataSetChanged();
}
}
LocationMapFragment.java
显示地图的静态图片的Fragment
public class LocationMapFragment extends Fragment {
private ImageView mImageView;
private final String URL = "http://api.map.baidu.com/staticimage?width=1000&height=1000¢er=%s,%s&zoom=15";
private DownloadTask mTask;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
//订阅 异步下载图片
@Subscribe(threadMode =ThreadMode.ASYNC)
public void onEvent(LocationEvent event) {
float longitude = event.longitude;
float latitude = event.latitude;
String url = String.format(URL, longitude, latitude);
mTask = new DownloadTask();
mTask.execute(url);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
mImageView = new ImageView(getActivity());
return mImageView;
}
private class DownloadTask extends AsyncTask {
@Override
protected Drawable doInBackground(String... params) {
String downloadUrl = params[0];
try {
return BitmapDrawable.createFromStream(new URL(downloadUrl).openStream(), "bitmap.jpg");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(Drawable drawable) {
super.onPostExecute(drawable);
mImageView.setImageDrawable(drawable);
}
}
}
MainAcitivity.java 展示两个fragment 清除list和随机产生一个经纬度的post
public class MainActivity extends AppCompatActivity {
private Button mClearButton;
private Button mMoveButton;
private float DEFAULT_LONGITUDE = 116.413554F;
private float DEFAULT_LATITUDE = 39.911013f;
private float longitude;
private float latitude;
private float OFFSET = 0.1f;
private Random random = new Random();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mClearButton = (Button)findViewById(R.id.bt_clear_location);
mMoveButton = (Button)findViewById(R.id.bt_move_location);
mClearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EventBus.getDefault().post(new ClearLocationEvent());
}
});
mMoveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
longitude = DEFAULT_LONGITUDE + OFFSET * random.nextFloat();
latitude = DEFAULT_LATITUDE + OFFSET * random.nextFloat();
EventBus.getDefault().post(new LocationEvent(longitude, latitude));
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
事件:
移动坐标的事件(封装的实体类,用来传递数据的)
LocationMoveEvent.java
public class LocationMoveEvent {
public float longitude;
public float latitude;
public LocationMoveEvent(float lng, float lat) {
this.longitude = lng;
this.latitude = lat;
}
}
LocationClearEvent.java 清除list的数据 这里比较简单,可以定义为空
/**
* 清除坐标的事件
*/
public class LocationClearEvent {
}
完成
EventBus. 事件响应有更多的线程选择
EventBus. 支持Sticky(黏贴型) Event 和 处理事件的优先级
- 最后我想说,可能EventBus和Otto很早以前就有了,现在RxJava就能实现这样的功能,但是对于不了解Rx技术的人来说,这些还是非常有用的,Rx技术虽好,虽然很新,如果没有搞懂的情况下,贸然使用估计会给你带来很大的困难。最好在有一个比较懂Rx技术的人的前提下,开始使用,提高自己。
简单的使用
基本的使用步骤就是如下4步:
在模块build.gradle中添加EventBus框架的依赖:
implementation 'org.greenrobot:eventbus:3.1.1'
public class MyEvent {}
public void onEventMainThread
EventBus.getDefault().register(this)
EventBus.getDefault().post(new MyEvent())