《Netkiller Android 手札》之 EventBus 使用详解
本文节选自电子书《Netkiller Android 手札》
Netkiller Android 手札
http://www.netkiller.cn/android/index.html
Mr. Neo Chan, 陈景峯(BG7NYT)
中国广东省深圳市望海路半岛城邦三期
518067
+86 13113668890<netkiller@msn.com>
$Id: book.xml 606 2013-05-29 09:52:58Z netkiller $
版权 © 2018 Neo Chan
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
|
| |
|
2018-10
我的系列文档
编程语言
第 47 章 EventBus
目录
47.1. 添加 EventBus 依赖到项目Gradle文件
http://greenrobot.org/eventbus
在EventBus中主要有以下三个成员:
Event:事件,可以自定义为任意对象,类似Message类的作用; Publisher:事件发布者,可以在任意线程、任意位置发布Event,已发布的Evnet则由EventBus进行分发; Subscriber:事件订阅者,接收并处理事件,需要通过register(this)进行注册,而在类销毁时要使用unregister(this)方法解注册。每个Subscriber可以定义一个或多个事件处理方法,其方法名可以自定义,但需要添加@Subscribe的注解,并指明ThreadMode(不写默认为Posting)。
47.1. 添加 EventBus 依赖到项目Gradle文件
Gradle:
implementation 'org.greenrobot:eventbus:3.1.1'
完整的例子
apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "cn.netkiller.eventbus" minSdkVersion 26 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'org.greenrobot:eventbus:3.1.1' }
47.2. 快速开始一个演示例子
操作 EventBus 只需四个步骤
1. 注册事件 EventBus.getDefault().register( this ); 2. 取消注册 EventBus.getDefault().unregister( this ); 3. 订阅事件 @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); } 4. 发送数据 EventBus.getDefault().post(new MessageEvent("Helloworld"));
47.2.1. 创建 MessageEvent 类
package cn.netkiller.eventbus.pojo; public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } }
47.2.2. Layout
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text="Button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" /> </android.support.constraint.ConstraintLayout>
47.2.3. Activity
package cn.netkiller.eventbus; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import cn.netkiller.eventbus.pojo.MessageEvent; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); EventBus.getDefault().register(this); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EventBus.getDefault().post(new MessageEvent("Hello everyone!")); } }); } @Override protected void onDestroy() { super.onDestroy(); //取消注册 , 防止Activity内存泄漏 EventBus.getDefault().unregister(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); } }
47.3. Sticky Events
Sticky Events 粘性事件可以理解为Message做了持久化,直到Message被消费为止。无需注册即可发送Message。
下面的例子:在MainActivity发送事件,在StickyActivity里注册并且接收事件
A. MainActivity 发送事件: EventBus.getDefault().postSticky(new MessageEvent("http://www.netkiller.cn")); B. StickyActivity 接收事件 1. 注册 EventBus.getDefault().register( this ); 2. 事件接收 @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); } 3. 取消注册 EventBus.getDefault().unregister( this ) ;
47.3.1. MainActivity
Layout
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text="Button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView" /> </android.support.constraint.ConstraintLayout>
MainActivity
package cn.netkiller.eventbus; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import cn.netkiller.eventbus.pojo.MessageEvent; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!")); startActivity(new Intent(MainActivity.this, StickyActivity.class)); } }); } }
47.3.2. StickyActivity
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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=".StickyActivity"> </android.support.constraint.ConstraintLayout>
StickyActivity
package cn.netkiller.eventbus; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import cn.netkiller.eventbus.pojo.MessageEvent; public class StickyActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sticky); EventBus.getDefault().register(this); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); } }
47.3.3. MessageEvent
package cn.netkiller.eventbus.pojo; public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } }
47.3.4. 删除粘性事件
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class); // Better check that an event was actually posted before if(stickyEvent != null) { // "Consume" the sticky event EventBus.getDefault().removeStickyEvent(stickyEvent); // Now do something with it }
47.4. 线程模型
EventBus 有五种线程模型(ThreadMode)
Posting:直接在事件发布者所在线程执行事件处理方法; Main:直接在主线程中执行事件处理方法(即UI线程),如果发布事件的线程也是主线程,那么事件处理方法会直接被调用,并且未避免ANR,该方法应避免进行耗时操作; MainOrdered:也是直接在主线程中执行事件处理方法,但与Main方式不同的是,不论发布者所在线程是不是主线程,发布的事件都会进入队列按事件串行顺序依次执行; BACKGROUND:事件处理方法将在后台线程中被调用。如果发布事件的线程不是主线程,那么事件处理方法将直接在该线程中被调用。如果发布事件的线程是主线程,那么将使用一个单独的后台线程,该线程将按顺序发送所有的事件。 Async:不管发布者的线程是不是主线程,都会开启一个新的线程来执行事件处理方法。如果事件处理方法的执行需要一些时间,例如网络访问,那么就应该使用该模式。为避免触发大量的长时间运行的事件处理方法,EventBus使用了一个线程池来有效地重用已经完成调用订阅者方法的线程以限制并发线程的数量。 后面会通过代码展示五种ThreadMode的工作方式。
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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=".ThreadModeActivity"> <Button android:id="@+id/buttonSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text="Send" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/buttonThread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text="Send Thread" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/buttonSend" /> </android.support.constraint.ConstraintLayout>
package cn.netkiller.eventbus; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; public class ThreadModeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread_mode); EventBus.getDefault().register(this); findViewById(R.id.buttonSend).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d("EventBus Thread : ", Thread.currentThread().getName()); EventBus.getDefault().post("http://www.netkiller.cn"); } }); findViewById(R.id.buttonThread).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { Log.d("EventBus Thread : ", Thread.currentThread().getName()); EventBus.getDefault().post("http://www.netkiller.cn"); } }).start(); } }); } @Subscribe(threadMode = ThreadMode.POSTING) public void onMessageEventPostThread(String event) { Log.d("EventBus PostThread", "Message: " + event + " thread: " + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEventMainThread(String event) { Log.d("EventBus MainThread", "Message: " + event + " thread: " + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) public void onEventMainOrdered(String event) { Log.d("EventBus MainOrdered", "Message: " + event + " thread:" + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEventBackgroundThread(String event) { Log.d("EventBus BackgroundThread", "Message: " + event + " thread: " + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onMessageEventAsync(String event) { Log.d("EventBus Async", "Message: " + event + " thread: " + Thread.currentThread().getName()); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } }
在 main 线程中发布消息
D/EventBus Thread :: main D/EventBus MainThread: Message: http://www.netkiller.cn thread: main D/EventBus PostThread: Message: http://www.netkiller.cn thread: main D/EventBus Async: Message: http://www.netkiller.cn thread: pool-1-thread-1 D/EventBus BackgroundThread: Message: http://www.netkiller.cn thread: pool-1-thread-2 D/EventBus MainOrdered: Message: http://www.netkiller.cn thread:main
在线程中发布消息
D/EventBus Thread :: Thread-2 D/EventBus BackgroundThread: Message: http://www.netkiller.cn thread: Thread-2 D/EventBus PostThread: Message: http://www.netkiller.cn thread: Thread-2 D/EventBus Async: Message: http://www.netkiller.cn thread: pool-1-thread-2 D/EventBus MainOrdered: Message: http://www.netkiller.cn thread:main D/EventBus MainThread: Message: http://www.netkiller.cn thread: main
47.5. 配置 EventBus
上面章节中的例子EventBus实例中采用默认方式
EventBus.getDefault().register(this);
这种方式的获取到的EventBus的都是默认属性,有时候并不能满足我们的要求,这时候我们可以通过EventBusBuilder来个性化配置EventBus的属性。
// 创建默认的EventBus对象,相当于EventBus.getDefault()。 EventBus installDefaultEventBus(): // 添加由EventBus“注释预处理器生成的索引 EventBuilder addIndex(SubscriberInfoIndex index): // 默认情况下,EventBus认为事件类有层次结构(订户超类将被通知) EventBuilder eventInheritance(boolean eventInheritance): // 定义一个线程池用于处理后台线程和异步线程分发事件 EventBuilder executorService(java.util.concurrent.ExecutorService executorService): // 设置忽略订阅索引,即使事件已被设置索引,默认为false EventBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex): // 打印没有订阅消息,默认为true EventBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages): // 打印订阅异常,默认true EventBuilder logSubscriberExceptions(boolean logSubscriberExceptions): // 设置发送的的事件在没有订阅者的情况时,EventBus是否保持静默,默认true EventBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent): // 发送分发事件的异常,默认true EventBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent): // 在3.0以前,接收处理事件的方法名以onEvent开头,方法名称验证避免不是以此开头,启用严格的方法验证(默认:false) EventBuilder strictMethodVerification(java.lang.Class<?> clazz) // 如果onEvent***方法出现异常,是否将此异常分发给订阅者(默认:false) EventBuilder throwSubscriberException(boolean throwSubscriberException)
我的实例参考
EventBus eventBus = EventBus.builder().eventInheritance(true) .ignoreGeneratedIndex(false) .logNoSubscriberMessages(true) .logSubscriberExceptions(false) .sendNoSubscriberEvent(true) .sendSubscriberExceptionEvent(true) .throwSubscriberException(false) .strictMethodVerification(true) .build(); eventBus.register(this);
47.6. 事件优先级
priority 数值越大优先级又高
// MainActivity @Subscribe(priority = 2) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); } // SecondActivity @Subscribe(priority = 1) public void onMessageSecondEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); }
时间拦截,MainActivity 收到信息后调用 EventBus.getDefault().cancelEventDelivery(event); 之后所有订阅将收不到信息。
// MainActivity @Subscribe(priority = 2) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); EventBus.getDefault().cancelEventDelivery(event); } // SecondActivity @Subscribe(priority = 1) public void onMessageSecondEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); }
47.7. 捕获异常事件
在 init() 中加入你的业务逻辑,根据需要,在特定的情况下使用 throw new Exception("异常信息"); 抛出异常。异常会被 hrowableFailureEvent(ThrowableFailureEvent event) 捕获到。
package cn.netkiller.eventbus; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Toast; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.util.AsyncExecutor; import org.greenrobot.eventbus.util.ThrowableFailureEvent; import cn.netkiller.eventbus.pojo.MessageEvent; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); EventBus.getDefault().register(this); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { AsyncExecutor.create().execute( new AsyncExecutor.RunnableEx() { @Override public void run() throws Exception { init(); EventBus.getDefault().post(new MessageEvent("Hello everyone!")); } } ); } }); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show(); } public void init() throws Exception { // ... throw new Exception("实际发送异常"); } @Subscribe(threadMode = ThreadMode.MAIN) public void hrowableFailureEvent(ThrowableFailureEvent event) { Log.d("EventBus", "hrowableFailureEvent: " + event.getThrowable().getMessage()); Toast.makeText(this, event.getThrowable().getMessage(), Toast.LENGTH_SHORT).show(); } }
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android-返回桌面?退出程序?
有时候我们希望用户双击返回键的时候,再去退出应用,那么怎么实现这种需求呢? 有时候我们希望用户点击一次返回键的时候,不是退出应用,而是返回到桌面,那么怎么实现这种效果呢? 如何管理整个应用的各个环节,做到退出应用呢? 在这里,对相关方法大概整理。 一、容器式 我们在BaseActivity 建立一个全局容器,把所有的Activity存储起来统一维护,另外可以在Base里可以处理一些界面的共同逻辑,退出时循环遍历finish所有Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 添加Activity到堆栈 BaseAtyContainer.getInstance().addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); // 结束Activity&从栈中移除该Activity BaseAtyContainer....
- 下一篇
Android-来填写一个验证码吧!(一)
熟悉web开发中童鞋们都知道为了防止恶意破解、恶意提交等行为,所以我们在提交表单数据时,都会使用随机验证码功能。在Android应用中我们同样需要这一功能,去有效的避开恶意注册,恶意攻击。那么该如何实现呢? 这里介绍一种,数字英文随机生成的图片验证码,使用很简单,一行代码,调用一个工具类,就能完美的实现图片验证的显示了。 效果如下: **具体思路如下:在一块固定宽高的画布上,画上固定个数的随机数字和字母,再画上固定条数的干扰线随机数和干扰线的颜色随机生成,随机数的样式随机生成。** 理清思路,接下来看下代码吧! public class CodeUtils { //随机码集 private static final char[] CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', '...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果