Android實現(xiàn)消息總線的幾種方式詳解
前言
消息總線又叫事件總線,為什么我們需要一個消息總線呢?是因為隨著項目變大,頁面變多,我們可能出現(xiàn)跨頁面、跨組件、跨線程、跨進程傳遞消息與數(shù)據(jù),為了更方便的直接通知到指定的頁面實現(xiàn)具體的邏輯,我們需要消息總線來實現(xiàn)。
從最基本的 BroadcastReceiver 到 EventBus 再到RxBus ,后來官方出了AndroidX jetpack 我們開始使用LiveDataBus,最后到Kotlin的流行出來了FlowBus。我們看看他們是怎么一步一步演變的。
一、BroadcastReceiver 廣播
我們再初入 Android 的時候都應(yīng)該學(xué)過廣播接收者,分為靜態(tài)廣播和動態(tài)注冊廣播,在高版本的 Android 中限制了我們一些靜態(tài)廣播的使用,不過我們還是能通過動態(tài)注冊的方式獲取一些系統(tǒng)的狀態(tài)改變。像常用的電量變化、網(wǎng)絡(luò)狀態(tài)變化、短信發(fā)送接收的狀態(tài)等等。
比如網(wǎng)絡(luò)變化的監(jiān)聽:
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
application.getApplicationContext().registerReceiver(InstanceHolder.INSTANCE, intentFilter);在消息中線中,我們可以使用本地廣播來實現(xiàn) LocalBroadcastManager 消息的通知。
LocalBroadcastManager mLocalBroadcastManager = LocalBroadcastManager.getInstance(mContext);
BroadcastReceiver mLoginReceiver = new LoginSuccessReceiver();
mLocalBroadcastManager.registerReceiver(mLoginReceiver, new IntentFilter(Constants.ACTION_LOGIN_SUCCESS));
private class LoginSuccessReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//刷新Home界面
refreshHomePage();
//刷新未讀信息
requestUnreadNum();
}
}
//記得要解綁對應(yīng)的接收器
mLocalBroadcastManager.unregisterReceiver(mLoginReceiver);這樣就可以實現(xiàn)一個消息通知了。相比 EventBus 它的性能和空間的消耗都是較大的,并且只能固定在主線程運行。
二、EventBus
EventBus最大的特點就是簡潔、解耦,可以直接傳遞我們自定義的消息Message。EventBus簡化了應(yīng)用程序內(nèi)各組件間、組件與后臺線程間的通信。記得2015年左右是非?;鸨摹?/p>
EventBus的調(diào)度靈活,不依賴于 Context,使用時無需像廣播一樣關(guān)注 Context 的注入與傳遞??衫^承、優(yōu)先級、粘滯,是 EventBus 比之于廣播的優(yōu)勢。幾乎可以滿足我們?nèi)康男枨蟆?/p>
最初的EventBus其實就是一個方法的集合與查找,核心是通過register方法把帶有@Subscrib注解的方法和參數(shù)之類的東西全部放入一個List集合,然后通過post方法去這個list循環(huán)查找到符合條件的方法去執(zhí)行。
如何使用EventBus,一共分5步:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_bus);
EventBus.getDefault().register(MainActivity.this); //1.注冊廣播
} @Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(MainActivity.this); //2.解注冊廣播
}/**
* 3.傳遞什么類型的。定義一個消息類
*/
public class MessageEvent {
public String name;
public MessageEvent(String name) {
this.name = name;
}
} @OnClick({R.id.bt_eventbus_send_main, R.id.bt_eventbus_send_sticky})
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_eventbus_send_main:
//4.發(fā)送消息
EventBus.getDefault().post(new MessageEvent("我是主頁面發(fā)送過來的消息"));
finish();
break;
}
} /**
* 5.接受到消息。需要注解
*
* @param event
*/
@Subscribe(threadMode = ThreadMode.MAIN) //主線程執(zhí)行
public void MessageEventBus(MessageEvent event) {
//5。顯示接受到的消息
mTvEventbusResult.setText(event.name);
}EventBus的性能開銷其實不大,EventBus2.4.0 版是利用反射來實現(xiàn)的,后來改成 APT 實現(xiàn)之后會好很多。主要問題是需要定義很多的消息對象,消息太多之后就感覺管理起來很麻煩。當(dāng)消息太多之后容器內(nèi)部的查找會出現(xiàn)性能瓶頸。
就算如此 EventBus 也是值得大家使用的。
三、RxBus
RxBus是基于RxJava實現(xiàn)的,強大是強大,但是學(xué)習(xí)成本比較高,需要額外導(dǎo)入RxJava RxAndroid等庫,這些庫體積還是較大的??梢詫崿F(xiàn)異步的消息等。
本身的實現(xiàn)是很簡單的:
public class RxBus {
private volatile static RxBus mDefaultInstance;
private final Subject<Object> mBus;
private RxBus() {
mBus = PublishSubject.create().toSerialized();
}
public static RxBus getInstance() {
if (mDefaultInstance == null) {
synchronized (RxBus.class) {
if (mDefaultInstance == null) {
mDefaultInstance = new RxBus();
}
}
}
return mDefaultInstance;
}
/**
* 發(fā)送事件
*/
public void post(Object event) {
mBus.onNext(event);
}
/**
* 根據(jù)傳遞的 eventType 類型返回特定類型(eventType)的 被觀察者
*/
public <T> Observable<T> toObservable(final Class<T> eventType) {
return mBus.ofType(eventType);
}
/**
* 判斷是否有訂閱者
*/
public boolean hasObservers() {
return mBus.hasObservers();
}
public void reset() {
mDefaultInstance = null;
}
}定義消息對象:
public class MsgEvent {
private String msg;
public MsgEvent(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}發(fā)送與接收:
RxBus.getInstance().toObservable(MsgEvent.class).subscribe(new Observer<MsgEvent>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(MsgEvent msgEvent) {
//處理事件
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
RxBus.getInstance().post(new MsgEvent("Java"));缺點是容易內(nèi)存泄露,我們需要使用rxlifecycle 或者使用CompositeDisposable 自己對生命周期進行處理解綁。
四、LiveDataBus
官方出了AndroidX jetpack 內(nèi)部包含LiveData,它可以感知并遵循Activity、Fragment或Service等組件的生命周期。
為什么要使用LiveDataBus,正是基于LiveData對組件生命周期可感知的特點,因此可以做到僅在組件處于生命周期的激活狀態(tài)時才更新UI數(shù)據(jù)。
一個簡單的LiveDataBus的實現(xiàn):
public final class LiveDataBus {
private final Map<String, BusMutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DEFAULT_BUS;
}
public <T> MutableLiveData<T> with(String key, Class<T> type) {
if (!bus.containsKey(key)) {
bus.put(key, new BusMutableLiveData<>());
}
return (MutableLiveData<T>) bus.get(key);
}
public MutableLiveData<Object> with(String key) {
return with(key, Object.class);
}
private static class ObserverWrapper<T> implements Observer<T> {
private Observer<T> observer;
public ObserverWrapper(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onChanged(@Nullable T t) {
if (observer != null) {
if (isCallOnObserve()) {
return;
}
observer.onChanged(t);
}
}
private boolean isCallOnObserve() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (stackTrace != null && stackTrace.length > 0) {
for (StackTraceElement element : stackTrace) {
if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&
"observeForever".equals(element.getMethodName())) {
return true;
}
}
}
return false;
}
}
private static class BusMutableLiveData<T> extends MutableLiveData<T> {
private Map<Observer, Observer> observerMap = new HashMap<>();
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void observeForever(@NonNull Observer<T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverWrapper(observer));
}
super.observeForever(observerMap.get(observer));
}
@Override
public void removeObserver(@NonNull Observer<T> observer) {
Observer realObserver = null;
if (observerMap.containsKey(observer)) {
realObserver = observerMap.remove(observer);
} else {
realObserver = observer;
}
super.removeObserver(realObserver);
}
private void hook(@NonNull Observer<T> observer) throws Exception {
//get wrapper's version
Class<LiveData> classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class<?> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
}注冊與發(fā)送:
LiveDataBus.get()
.with("key_test", String.class)
.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
}
});
LiveDataBus.get().with("key_test").setValue(s);LiveDataBus已經(jīng)算是很好用的,自動注冊解綁,根據(jù)Key傳遞泛型T對象,容易查找對應(yīng)的接收者,也可以實現(xiàn)可見的觸發(fā)和直接觸發(fā),可以實現(xiàn)跨進程,
LiveData有幾點不足,只能在主線程更新數(shù)據(jù),操作符無法轉(zhuǎn)換數(shù)據(jù),基于 Android Api 實現(xiàn)的,換一個平臺無法適應(yīng),基于這幾點又開發(fā)出了FlowBus。
五、FlowBus
很多人都說Flow 的出現(xiàn)導(dǎo)致 LiveData 沒那么重要了,就是因為 LiveData 的場景 都可以使用 Flow 平替了,還能更為的強大和靈活。
StateFlow 可以 替代ViewModel中傳遞數(shù)據(jù),SharedFlow 可以實現(xiàn)事件總線。(這兩者的異同如果大家有興趣,我可以單獨開一篇講下)。
SharedFlow 就是一種熱流,可以實現(xiàn)一對多的關(guān)系,其構(gòu)造方法支持天然支持普通的消息發(fā)送與粘性的消息發(fā)送。一般我們FlowBus都是基于 SharedFlow 來實現(xiàn):
object FlowBus {
private val busMap = mutableMapOf<String, EventBus<*>>()
private val busStickMap = mutableMapOf<String, StickEventBus<*>>()
@Synchronized
fun <T> with(key: String): EventBus<T> {
var eventBus = busMap[key]
if (eventBus == null) {
eventBus = EventBus<T>(key)
busMap[key] = eventBus
}
return eventBus as EventBus<T>
}
@Synchronized
fun <T> withStick(key: String): StickEventBus<T> {
var eventBus = busStickMap[key]
if (eventBus == null) {
eventBus = StickEventBus<T>(key)
busStickMap[key] = eventBus
}
return eventBus as StickEventBus<T>
}
//真正實現(xiàn)類
open class EventBus<T>(private val key: String) : LifecycleObserver {
//私有對象用于發(fā)送消息
private val _events: MutableSharedFlow<T> by lazy {
obtainEvent()
}
//暴露的公有對象用于接收消息
val events = _events.asSharedFlow()
open fun obtainEvent(): MutableSharedFlow<T> = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST)
//主線程接收數(shù)據(jù)
fun register(lifecycleOwner: LifecycleOwner, action: (t: T) -> Unit) {
lifecycleOwner.lifecycle.addObserver(this)
lifecycleOwner.lifecycleScope.launch {
events.collect {
try {
action(it)
} catch (e: Exception) {
e.printStackTrace()
YYLogUtils.e("FlowBus - Error:$e")
}
}
}
}
//協(xié)程中發(fā)送數(shù)據(jù)
suspend fun post(event: T) {
_events.emit(event)
}
//主線程發(fā)送數(shù)據(jù)
fun post(scope: CoroutineScope, event: T) {
scope.launch {
_events.emit(event)
}
}
//自動銷毀
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
YYLogUtils.w("FlowBus - 自動onDestroy")
val subscriptCount = _events.subscriptionCount.value
if (subscriptCount <= 0)
busMap.remove(key)
}
}
class StickEventBus<T>(key: String) : EventBus<T>(key) {
override fun obtainEvent(): MutableSharedFlow<T> = MutableSharedFlow(1, 1, BufferOverflow.DROP_OLDEST)
}
}發(fā)送與接收消息
// 主線程-發(fā)送消息
FlowBus.with<String>("test-key-01").post(this@Demo11OneFragment2.lifecycleScope, "Test Flow Bus Message") // 接收消息
FlowBus.with<String>("test-key-01").register(this) {
LogUtils.w("收到FlowBus消息 - " + it)
}發(fā)送粘性消息
FlowBus.withStick<String>("test-key-02").post(lifecycleScope, "Test Stick Message") FlowBus.withStick<String>("test-key-02").register(this){
LogUtils.w("收到粘性消息:$it")
}Log如下:


總結(jié)
其實這么多消息總線框架,我更推薦EventBus LiveDataBus FlowBus這三種。
總的來說,我們盡量不依賴第三方的框架來實現(xiàn),那么 FlowBus 是語言層級的,基于Kotlin的特性實現(xiàn),比較推薦了,我本人是喜歡用LiveDataBus,基于 Android 開發(fā)場景來看,幾乎能滿足全部要求了。
如果大家有源碼方面的需求可以看看這里,上面的源碼也都貼出來了,不過更推薦大家根據(jù)不同的類型自行去 Github 上面找對應(yīng)的實現(xiàn)封裝,功能會更多,健壯性更好。
到此這篇關(guān)于Android實現(xiàn)消息總線的幾種方式的文章就介紹到這了,更多相關(guān)Android消息總線實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android RecyclerView 復(fù)用錯亂通用解法詳解
本篇文章主要介紹了Android RecyclerView 復(fù)用錯亂通用解法詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Android 沉浸式狀態(tài)欄與隱藏導(dǎo)航欄實例詳解
沉浸式狀態(tài)欄是指狀態(tài)欄與ActionBar顏色相匹配,隱藏導(dǎo)航欄,就是將導(dǎo)航欄隱藏,去掉下面的黑條。下面通過實例給大家詳解android沉浸式狀態(tài)欄與隱藏導(dǎo)航欄,感興趣的朋友一起看看2017-07-07
android開發(fā)教程之textview內(nèi)容超出屏幕寬度顯示省略號
android開發(fā)中用textview顯示內(nèi)容時,顯示內(nèi)容過多可能會折行或顯示不全,那樣效果很不好,我們可以用省略號顯示,下面看設(shè)置方法2014-02-02

