版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
【移動應用開發(fā)技術】如何用LiveDataBus替代RxBus
這篇文章將為大家詳細講解有關如何用LiveDataBus替代RxBus,文章內(nèi)容質(zhì)量較高,因此在下分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。對于Android系統(tǒng)來說,消息傳遞是最基本的組件,每一個App內(nèi)的不同頁面,不同組件都在進行消息傳遞。消息傳遞既可以用于Android四大組件之間的通信,也可用于異步線程和主線程之間的通信。對于Android開發(fā)者來說,經(jīng)常使用的消息傳遞方式有很多種,從最早使用的Handler、BroadcastReceiver、接口回調(diào),到近幾年流行的通信總線類框架EventBus、RxBus。Android消息傳遞框架,總在不斷的演進之中。從EventBus說起EventBus是一個Android事件發(fā)布/訂閱框架,通過解耦發(fā)布者和訂閱者簡化Android事件傳遞。EventBus可以代替Android傳統(tǒng)的Intent、Handler、Broadcast或接口回調(diào),在Fragment、Activity、Service線程之間傳遞數(shù)據(jù),執(zhí)行方法。EventBus最大的特點就是簡潔、解耦。在沒有EventBus之前我們通常用廣播來實現(xiàn)監(jiān)聽,或者自定義接口函數(shù)回調(diào),有的場景我們也可以直接用Intent攜帶簡單數(shù)據(jù),或者在線程之間通過Handler處理消息傳遞。但無論是廣播還是Handler機制遠遠不能滿足我們高效的開發(fā)。EventBus簡化了應用程序內(nèi)各組件間、組件與后臺線程間的通信。EventBus一經(jīng)推出,便受到廣大開發(fā)者的推崇?,F(xiàn)在看來,EventBus給Android開發(fā)者世界帶來了一種新的框架和思想,就是消息的發(fā)布和訂閱。這種思想在其后很多框架中都得到了應用。圖片摘自EventBusGitHub主頁發(fā)布/訂閱模式訂閱發(fā)布模式定義了一種“一對多”的依賴關系,讓多個訂閱者對象同時監(jiān)聽某一個主題對象。這個主題對象在自身狀態(tài)變化時,會通知所有訂閱者對象,使它們能夠自動更新自己的狀態(tài)。RxBus的出現(xiàn)RxBus不是一個庫,而是一個文件,實現(xiàn)只有短短30行代碼。RxBus本身不需要過多分析,它的強大完全來自于它基于的RxJava技術。響應式編程(ReactiveProgramming)技術這幾年特別火,RxJava是它在Java上的實作。RxJava天生就是發(fā)布/訂閱模式,而且很容易處理線程切換。所以,RxBus憑借區(qū)區(qū)30行代碼,就敢挑戰(zhàn)EventBus“江湖老大”的地位。RxBus原理在RxJava中有個Subject類,它繼承Observable類,同時實現(xiàn)了Observer接口,因此Subject可以同時擔當訂閱者和被訂閱者的角色,我們使用Subject的子類PublishSubject來創(chuàng)建一個Subject對象(PublishSubject只有被訂閱后才會把接收到的事件立刻發(fā)送給訂閱者),在需要接收事件的地方,訂閱該Subject對象,之后如果Subject對象接收到事件,則會發(fā)射給該訂閱者,此時Subject對象充當被訂閱者的角色。完成了訂閱,在需要發(fā)送事件的地方將事件發(fā)送給之前被訂閱的Subject對象,則此時Subject對象作為訂閱者接收事件,然后會立刻將事件轉發(fā)給訂閱該Subject對象的訂閱者,以便訂閱者處理相應事件,到這里就完成了事件的發(fā)送與處理。最后就是取消訂閱的操作了,RxJava中,訂閱操作會返回一個Subscription對象,以便在合適的時機取消訂閱,防止內(nèi)存泄漏,如果一個類產(chǎn)生多個Subscription對象,我們可以用一個CompositeSubscription存儲起來,以進行批量的取消訂閱。RxBus有很多實現(xiàn),如:AndroidKnife/RxBus(
/AndroidKnife/RxBus
)Blankj/RxBus(
/Blankj/RxBus
)其實正如前面所說的,RxBus的原理是如此簡單,我們自己都可以寫出一個RxBus的實現(xiàn):基于RxJava1的RxBus實現(xiàn):public
final
class
RxBus
{
private
final
Subject<Object,
Object>
bus;
private
RxBus()
{
bus
=
new
SerializedSubject<>(PublishSubject.create());
}
private
static
class
SingletonHolder
{
private
static
final
RxBus
defaultRxBus
=
new
RxBus();
}
public
static
RxBus
getInstance()
{
return
SingletonHolder.defaultRxBus;
}
/*
*
發(fā)送
*/
public
void
post(Object
o)
{
bus.onNext(o);
}
/*
*
是否有Observable訂閱
*/
public
boolean
hasObservable()
{
return
bus.hasObservers();
}
/*
*
轉換為特定類型的Obserbale
*/
public
<T>
Observable<T>
toObservable(Class<T>
type)
{
return
bus.ofType(type);
}
}基于RxJava2的RxBus實現(xiàn):public
final
class
RxBus2
{
private
final
Subject<Object>
bus;
private
RxBus2()
{
//
toSerialized
method
made
bus
thread
safe
bus
=
PublishSubject.create().toSerialized();
}
public
static
RxBus2
getInstance()
{
return
Holder.BUS;
}
private
static
class
Holder
{
private
static
final
RxBus2
BUS
=
new
RxBus2();
}
public
void
post(Object
obj)
{
bus.onNext(obj);
}
public
<T>
Observable<T>
toObservable(Class<T>
tClass)
{
return
bus.ofType(tClass);
}
public
Observable<Object>
toObservable()
{
return
bus;
}
public
boolean
hasObservers()
{
return
bus.hasObservers();
}
}引入LiveDataBus的想法從LiveData談起LiveData是AndroidArchitectureComponents提出的框架。LiveData是一個可以被觀察的數(shù)據(jù)持有類,它可以感知并遵循Activity、Fragment或Service等組件的生命周期。正是由于LiveData對組件生命周期可感知特點,因此可以做到僅在組件處于生命周期的激活狀態(tài)時才更新UI數(shù)據(jù)。LiveData需要一個觀察者對象,一般是Observer類的具體實現(xiàn)。當觀察者的生命周期處于STARTED或RESUMED狀態(tài)時,LiveData會通知觀察者數(shù)據(jù)變化;在觀察者處于其他狀態(tài)時,即使LiveData的數(shù)據(jù)變化了,也不會通知。LiveData的優(yōu)點UI和實時數(shù)據(jù)保持一致,因為LiveData采用的是觀察者模式,這樣一來就可以在數(shù)據(jù)發(fā)生改變時獲得通知,更新UI。避免內(nèi)存泄漏,觀察者被綁定到組件的生命周期上,當被綁定的組件銷毀(destroy)時,觀察者會立刻自動清理自身的數(shù)據(jù)。不會再產(chǎn)生由于Activity處于stop狀態(tài)而引起的崩潰,例如:當Activity處于后臺狀態(tài)時,是不會收到LiveData的任何事件的。不需要再解決生命周期帶來的問題,LiveData可以感知被綁定的組件的生命周期,只有在活躍狀態(tài)才會通知數(shù)據(jù)變化。實時數(shù)據(jù)刷新,當組件處于活躍狀態(tài)或者從不活躍狀態(tài)到活躍狀態(tài)時總是能收到最新的數(shù)據(jù)。解決ConfigurationChange問題,在屏幕發(fā)生旋轉或者被回收再次啟動,立刻就能收到最新的數(shù)據(jù)。談一談AndroidArchitectureComponentsAndroidArchitectureComponents的核心是Lifecycle、LiveData、ViewModel以及Room,通過它可以非常優(yōu)雅的讓數(shù)據(jù)與界面進行交互,并做一些持久化的操作,高度解耦,自動管理生命周期,而且不用擔心內(nèi)存泄漏的問題。Room
一個強大的SQLite對象映射庫。ViewModel一類對象,它用于為UI組件提供數(shù)據(jù),在設備配置發(fā)生變更時依舊可以存活。LiveData
一個可感知生命周期、可被觀察的數(shù)據(jù)容器,它可以存儲數(shù)據(jù),還會在數(shù)據(jù)發(fā)生改變時進行提醒。Lifecycle包含LifeCycleOwer和LifecycleObserver,分別是生命周期所有者和生命周期感知者。AndroidArchitectureComponents的特點數(shù)據(jù)驅(qū)動型編程變化的永遠是數(shù)據(jù),界面無需更改。感知生命周期,防止內(nèi)存泄漏高度解耦數(shù)據(jù),界面高度分離。數(shù)據(jù)持久化數(shù)據(jù)、ViewModel不與UI的生命周期掛鉤,不會因為界面的重建而銷毀。重點:為什么使用LiveData構建數(shù)據(jù)通信總線LiveDataBus使用LiveData的理由LiveData具有的這種可觀察性和生命周期感知的能力,使其非常適合作為Android通信總線的基礎構件。使用者不用顯示調(diào)用反注冊方法。由于LiveData具有生命周期感知能力,所以LiveDataBus只需要調(diào)用注冊回調(diào)方法,而不需要顯示的調(diào)用反注冊方法。這樣帶來的好處不僅可以編寫更少的代碼,而且可以完全杜絕其他通信總線類框架(如EventBus、RxBus)忘記調(diào)用反注冊所帶來的內(nèi)存泄漏的風險。為什么要用LiveDataBus替代EventBus和RxBusLiveDataBus的實現(xiàn)極其簡單,相對EventBus復雜的實現(xiàn),LiveDataBus只需要一個類就可以實現(xiàn)。LiveDataBus可以減小APK包的大小,由于LiveDataBus只依賴Android官方AndroidArchitectureComponents組件的LiveData,沒有其他依賴,本身實現(xiàn)只有一個類。作為比較,EventBusJAR包大小為57kb,RxBus依賴RxJava和RxAndroid,其中RxJava2包大小2.2MB,RxJava1包大小1.1MB,RxAndroid包大小9kb。使用LiveDataBus可以大大減小APK包的大小。LiveDataBus依賴方支持更好,LiveDataBus只依賴Android官方AndroidArchitectureComponents組件的LiveData,相比RxBus依賴的RxJava和RxAndroid,依賴方支持更好。LiveDataBus具有生命周期感知,LiveDataBus具有生命周期感知,在Android系統(tǒng)中使用調(diào)用者不需要調(diào)用反注冊,相比EventBus和RxBus使用更為方便,并且沒有內(nèi)存泄漏風險。LiveDataBus的設計和架構LiveDataBus的組成消息消息可以是任何的Object,可以定義不同類型的消息,如Boolean、String。也可以定義自定義類型的消息。消息通道LiveData扮演了消息通道的角色,不同的消息通道用不同的名字區(qū)分,名字是String類型的,可以通過名字獲取到一個LiveData消息通道。消息總線消息總線通過單例實現(xiàn),不同的消息通道存放在一個HashMap中。訂閱訂閱者通過getChannel獲取消息通道,然后調(diào)用observe訂閱這個通道的消息。發(fā)布發(fā)布者通過getChannel獲取消息通道,然后調(diào)用setValue或者postValue發(fā)布消息。LiveDataBus原理圖LiveDataBus的實現(xiàn)第一個實現(xiàn):public
final
class
LiveDataBus
{
private
final
Map<String,
MutableLiveData<Object>>
bus;
private
LiveDataBus()
{
bus
=
new
HashMap<>();
}
private
static
class
SingletonHolder
{
private
static
final
LiveDataBus
DATA_BUS
=
new
LiveDataBus();
}
public
static
LiveDataBus
get()
{
return
SingletonHolder.DATA_BUS;
}
public
<T>
MutableLiveData<T>
getChannel(String
target,
Class<T>
type)
{
if
(!bus.containsKey(target))
{
bus.put(target,
new
MutableLiveData<>());
}
return
(MutableLiveData<T>)
bus.get(target);
}
public
MutableLiveData<Object>
getChannel(String
target)
{
return
getChannel(target,
Object.class);
}
}短短二十行代碼,就實現(xiàn)了一個通信總線的全部功能,并且還具有生命周期感知功能,并且使用起來也及其簡單:注冊訂閱:LiveDataBus.get().getChannel("key_test",
Boolean.class)
.observe(this,
new
Observer<Boolean>()
{
@Override
public
void
onChanged(@Nullable
Boolean
aBoolean)
{
}
});發(fā)送消息:LiveDataBus.get().getChannel("key_test").setValue(true);我們發(fā)送了一個名為"key_test",值為true的事件。這個時候訂閱者就會收到消息,并作相應的處理,非常簡單。問題出現(xiàn)對于LiveDataBus的第一版實現(xiàn),我們發(fā)現(xiàn),在使用這個LiveDataBus的過程中,訂閱者會收到訂閱之前發(fā)布的消息。對于一個消息總線來說,這是不可接受的。無論EventBus或者RxBus,訂閱方都不會收到訂閱之前發(fā)出的消息。對于一個消息總線,LiveDataBus必須要解決這個問題。問題分析怎么解決這個問題呢?先分析下原因:當LifeCircleOwner的狀態(tài)發(fā)生變化的時候,會調(diào)用LiveData.ObserverWrapper的activeStateChanged函數(shù),如果這個時候ObserverWrapper的狀態(tài)是active,就會調(diào)用LiveData的dispatchingValue。在LiveData的dispatchingValue中,又會調(diào)用LiveData的considerNotify方法。在LiveData的considerNotify方法中,紅框中的邏輯是關鍵,如果ObserverWrapper的mLastVersion小于LiveData的mVersion,就會去回調(diào)mObserver的onChanged方法。而每個新的訂閱者,其version都是-1,LiveData一旦設置過其version是大于-1的(每次LiveData設置值都會使其version加1),這樣就會導致LiveDataBus每注冊一個新的訂閱者,這個訂閱者立刻會收到一個回調(diào),即使這個設置的動作發(fā)生在訂閱之前。問題原因總結對于這個問題,總結一下發(fā)生的核心原因。對于LiveData,其初始的version是-1,當我們調(diào)用了其setValue或者postValue,其vesion會+1;對于每一個觀察者的封裝ObserverWrapper,其初始version也為-1,也就是說,每一個新注冊的觀察者,其version為-1;當LiveData設置這個ObserverWrapper的時候,如果LiveData的version大于ObserverWrapper的version,LiveData就會強制把當前value推送給Observer。如何解決這個問題明白了問題產(chǎn)生的原因之后,我們來看看怎么才能解決這個問題。很顯然,根據(jù)之前的分析,只需要在注冊一個新的訂閱者的時候把Wrapper的version設置成跟LiveData的version一致即可。那么怎么實現(xiàn)呢,看看LiveData的observe方法,他會在步驟1創(chuàng)建一個LifecycleBoundObserver,LifecycleBoundObserver是ObserverWrapper的派生類。然后會在步驟2把這個LifecycleBoundObserver放入一個私有Map容器mObservers中。無論ObserverWrapper還是LifecycleBoundObserver都是私有的或者包可見的,所以無法通過繼承的方式更改LifecycleBoundObserver的version。那么能不能從Map容器mObservers中取到LifecycleBoundObserver,然后再更改version呢?答案是肯定的,通過查看SafeIterableMap的源碼我們發(fā)現(xiàn)有一個protected的get方法。因此,在調(diào)用observe的時候,我們可以通過反射拿到LifecycleBoundObserver,再把LifecycleBoundObserver的version設置成和LiveData一致即可。對于非生命周期感知的observeForever方法來說,實現(xiàn)的思路是一致的,但是具體的實現(xiàn)略有不同。observeForever的時候,生成的wrapper不是LifecycleBoundObserver,而是AlwaysActiveObserver(步驟1),而且我們也沒有機會在observeForever調(diào)用完成之后再去更改AlwaysActiveObserver的version,因為在observeForever方法體內(nèi),步驟3的語句,回調(diào)就發(fā)生了。那么對于observeForever,如何解決這個問題呢?既然是在調(diào)用內(nèi)回調(diào)的,那么我們可以寫一個ObserverWrapper,把真正的回調(diào)給包裝起來。把ObserverWrapper傳給observeForever,那么在回調(diào)的時候我們?nèi)z查調(diào)用棧,如果回調(diào)是observeForever方法引起的,那么就不回調(diào)真正的訂閱者。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);
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 中學學生社團活動經(jīng)費管理流程制度
- 企業(yè)會計財務制度
- 2026年國際貿(mào)易實務操作模擬題及答案詳解
- 2026年傳統(tǒng)藝術文化古風舞蹈培訓活動教材配套教學與檢測試題庫
- 2026年城市排水監(jiān)測實驗室資質(zhì)考試復習題
- 2026年電氣工程師電動機原理與維護實操練習題202X
- 2025年刷臉支付設備定期維護協(xié)議
- 酒店地震應急演練方案4篇,酒店地震應急預案演練方案
- 急診護理中創(chuàng)傷性休克的急救處理流程及制度
- 安徽省安慶市岳西縣部分學校聯(lián)考2025-2026學年八年級上學期2月期末歷史試題(含答案)
- 新版-八年級上冊數(shù)學期末復習計算題15天沖刺練習(含答案)
- 2025智慧城市低空應用人工智能安全白皮書
- 云南師大附中2026屆高三月考試卷(七)地理
- 通信管道施工質(zhì)量控制方案
- 仁愛科普版(2024)八年級上冊英語Unit1~Unit6單元話題作文練習題(含答案+范文)
- 安徽寧馬投資有限責任公司2025年招聘派遣制工作人員考試筆試模擬試題及答案解析
- 2024-2025學年云南省昆明市五華區(qū)高一上學期期末質(zhì)量監(jiān)測歷史試題(解析版)
- 建筑坍塌應急救援規(guī)程
- 胰腺常見囊性腫瘤的CT診斷
- 房屋尾款交付合同(標準版)
- 檢測設備集成優(yōu)化方案
評論
0/150
提交評論