《NetkillerAndroid手札》之EventBus使用詳解
本文節選自電子書《Netkiller Android 手札》
Netkiller Android 手札
http://www.netkiller.cn/android/index.html
Mr. Neo Chan, 陳景峰(BG7NYT)
中國廣東省深圳市望海路半島城邦三期
518067
+86 13113668890
$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(); } }
相關文章
- Android訊息傳遞之EventBus 3.0使用詳解Android
- Android EventBus3.x的使用詳解AndroidS3
- EventBus詳解及原始碼分析原始碼
- EventBus詳解及簡單例項單例
- Android EventBus 3.0 框架用法詳解Android框架
- EventBus 3.0+ 原始碼詳解(史上最詳細圖文講解)原始碼
- 常用輪子之EventBus基本使用及原理
- Flutter之DataTable使用詳解Flutter
- 安卓EventBus使用方法,eventbus舉例安卓
- Vue事件匯流排(EventBus)使用詳細介紹Vue事件
- EventBus3.0解析之註解處理器S3
- vue 元件之間通訊eventBus使用方法Vue元件
- Spring JdbcTemplate之使用詳解SpringJDBC
- Flutter之MaterialApp使用詳解FlutterAPP
- Android 註解系列之 EventBus3 原理(四)AndroidS3
- Android 中 EventBus 的使用(1):為什麼要使用 EventBusAndroid
- Flutter系列之Platform Channel使用詳解FlutterPlatform
- Jetpack 之 LifeCycle 元件使用詳解Jetpack元件
- Python之Requests模組使用詳解Python
- Android 註解系列之EventBus3“加速引擎“(五)AndroidS3
- Google guava原始碼之EventBusGoGuava原始碼
- EventBus的使用與分析
- 三方庫原始碼筆記(1)-EventBus 原始碼詳解原始碼筆記
- .Net使用Redis詳解之ServiceStack.RedisRedis
- RxBus-實現EventBus之Sticky
- RxBus-實現EventBus之post
- EventBus原始碼解讀詳細註釋(1)register的幕後黑手原始碼
- 簡單跨程式使用EventBus
- 淺談EventBus的使用原理
- 使用RxJava代替EventBus類庫RxJava
- 深入理解mysql之left join 使用詳解MySql
- Django之使用者認證模組詳解Django
- k8s之Service詳解-Service使用K8S
- Linux 三劍客之 grep 使用詳解Linux
- ORM框架之GreenDao3.0使用詳解(二)ORM框架
- Linux之sort命令使用入門詳解Linux
- .Net使用Redis詳解之ServiceStack.Redis(七)Redis
- JavaScript之this詳解JavaScript