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业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
solidity语言开发以太坊智能合约中的继承
我们已经探索了很多主题,在编写智能合约时我们发现经常使用相同的模式:例如,智能合约具有在构造函数中设置的所有者,然后生成修改器以便仅让所有者使用一些功能。如果我们制定实施这些功能的基础合约并在未来的智能合约中重复使用它们那该怎么办?你一定猜得到,我们将使用继承。 在Solidity中,继承与经典的面向对象编程语言非常相似。你首先编写基本智能合约并告知你的新智能合约将从基础合约继承。 你还必须通过复制包含多态的代码来了解Solidity支持多重继承。所有函数调用都是虚函数,这意味着会是调用派生函数最多的函数,除非明确给出了合约名称。当某一个智能合约从多个合约继承时,只在区块链上创建一个智能合约,并将所有基础合约中的代码复制到创建的智能合约中。 让我们写下我们的基本智能合约:它将让我们轻松地为我们的合约添加所有权。我们将其命名为Ownable。OpenZeppelin的员工写了很多可以在智能合约中使用的可重用代码。这些代码段可通过其工具或其Github存储库获得。 这是代码: pragma solidity ^0.4.11; /** * @title Ownable * @dev The ...
- 下一篇
优雅的在React项目中使用Redux
或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在React项目中使用Redux react-thunk 中间件,作用:支持异步action |--src |-- store Redux目录 |-- actions.js |-- index.js |-- reducers.js |-- state.js |-- components 组件目录 |-- Test.jsx |-- App.js 项目入口 准备工作 第1步:提供默认值,既然用Redux来管理数据,那么数据就一定要有默认值,所以我们将state的默认值统一放置在state.js文件: // state.js // 声明默认值 // 这里我们列举两个示例 // 同步数据:pageTitle // 异步数据:infoList(将来用异步接口获取) export default { pageTitle: '首页', i...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS6,CentOS7官方镜像安装Oracle11G
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Linux系统CentOS6、CentOS7手动修改IP地址