這也許是 RxBus 最友好的文章
本人之前做過一款社交類軟件,需要讓“未讀消息數”實時顯示到應用程序內(類似QQ,微信)。沒做過社交軟件的同學也許會認為這個功能很好做,其實不然。這個功能雖然邏輯上簡單,但是實際操作起來會很麻煩。我來簡單描述一下這個需求到底有多麻煩:如果一個好友給你發了一條消息,而此時你的應用程序不在前臺,但是沒辦法,這個時候你必須要去更新一下未讀消息數角標。那怎么辦呢???又或者,你接收到新消息之后,你當前所處的線程并非UI主線程,在這種情況下,你還是要去更新UI,會顯得十分尷尬。不是說子線程不能去更新UI,而是在子線程操作UI會十分麻煩,冗余代碼也會很多。
遇到這種需要不斷去更新UI的情況,基本上會采取以下三種做法。
做法一:很多同學都會開啟一個后臺service,并持有MainActivity的引用,每次接收到消息,就讓service去調用MainActivity的相關方法,更新MainActivity的UI,與此同時,再new一個Notification出來,告訴用戶,新消息來了。
做法二:可能大部分人都會想到這個做法,就是用一個BoradcastReceiver去接收新消息,然后再把消息的全部內容封裝到一個intent里面去,通過content.startActivity(intent)方法把intent傳到對應的Activity。對應的Activity接收到Intent之后,會去解析這個intent。這個做法相對于“做法一”會靈活很多。
做法三:在需要更新UI的地方定義一個接口,然后每次更新UI的時候都去調用這個接口。這個做法也是可取的,但是一不小心就出現內存泄漏的問題。
如果是我的話,更傾向于第二種做法,為什么呢?因為比較靈活,而且不容易發生內存泄漏的問題。
當時我也是確實是這么做的,但是我思考了幾天,發現這么做不是很好,但是說不出一個理由來。。。。。。可能是因為這種做法代碼比較多,而且不容易被別人讀懂吧
查了幾天資料,發現用EventBus/otto/RxBus可以很好的解決這個問題,綜合它們的優缺點,我最終選擇了RxBus。
RxBus主要用來處理應用程序間各個組件的通信,或者組件與組建之間的數據傳遞。(不用再像BroadcastReceiver一樣,把數據封裝到intent里面再傳遞出去了)
首先,為什么叫他RxBus?其實我也不知道,當初我以為RxBus是一個封裝好的庫,直接拿過來用就好了。事實并非如此,我找了半天沒找到現成的庫,都是教你如何去通過RxJava來構造一個RxBus,既然這樣,那我也就跟著他們做了。其實說到底,RxBus學的是一種思路,而并不是給你一個現成的庫,然后直接去調用。好了,廢話不多說,開始我們今天的任務吧!
對RxJava還不熟悉的同學請繞道,以下內容針對有RxJava基礎的同學。
RxJava內部實現其實還蠻復雜的,我暫時沒有時間去看源碼,但是思路還是蠻簡單的,就是觀察者模式。觀察者模式我在之前的文章也寫過,還不太清楚的同學可以去看一下我的另外一篇文章 《超詳細:常用的設計模式匯總》 。那我們如何去巧妙地去使用RxJava,實現一個Rxbus呢?看一下我的代碼。
public enum RxBus {
INSTANCE;
private Subject<Object, Object> mRxBusObserverable = new SerializedSubject<>(PublishSubject.create());
public static void send(Object object) {
if (INSTANCE.mRxBusObserverable.hasObservers()) {
INSTANCE.mRxBusObserverable.onNext(object);
}
}
public static Observable<Object> toObserverable() {
return INSTANCE.mRxBusObserverable;
}
}
我直接寫成了一個單例(單例模式寫法很多,不要糾結于我的這種寫法,你們完全可以換一種寫法),然后里面有兩個方法,一個是send,一個是toObserable。send方法很簡單,就是用來發送一條消息的,消息類型是Object類型,其實也就是說,可以發送任何消息。那么toObserable是干嘛的呢?就是用來訂閱消息的,如果不訂閱的話,send方法就沒有任何意義了。就好比一個女生,沒有一個男生去關注她,她還在那里搔首弄姿的話,顯得很白癡!
send方法是static類型的,所以在程序的任何地方都可以調用。調用之后,代表一條信息發出去了,只要是訂閱過的人,都可以接收到。
toObserverable也是static類型的,所以在程序的任何地方都可以調用。調用之后,就說明訂閱成功了。如果我這個時候send一下,那就會接收到消息了。
說再多都是屁!那么我們來看一下如何使用這個類。
我們先調用toObserverable這個方法,還是那句話,如果不訂閱的話,send就沒有意義了。那我們什么時候去訂閱呢?誰去訂閱呢?訂閱之后要干嘛呢?
看代碼便知:
public abstract class BaseActivity extends AppCompatActivity {
private Subscription subscription;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initRxBus(setOnNext());
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindRxBus();
}
private Action1<Object> setOnNext() {
return new Action1<Object>() {
@Override
public void call(Object o) {
doOnNext(o);
}
};
}
protected abstract void doOnNext(Object o);
private void initRxBus(final Action1<Object> onNext) {
if (onNext != null) {
subscription = RxBus.toObserverable()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onNext);
}
}
private void unbindRxBus() {
if (subscription != null) {
subscription.unsubscribe();
}
}
}
自認為這段代碼理解起來還是蠻有意思的。從整體上來看,onCreate方法初始化RxBus,onDestory方法解綁RxBus。換句話說,其實就是在onCreate中訂閱一下,在onDestory中取消訂閱,意思就是說,MainActivity已經開始關注RxBus了,RxBus只要一有動靜,MainActivity就能馬上知道。取消訂閱是為了防止程序內存泄漏。取消訂閱的代碼很簡單,我不多解釋。我們主要來看一下訂閱的代碼。
initRxBus(setOnNext());
先看一下initRxBus(final Aciton1<Object> onNext)這個方法,其實里面就是簡單調用了RxBus的訂閱方法,然后用Schedulers切換了線程,最后subscribe一下。接觸過RxJava的同學,這段代碼理解起來應該不是很費勁。
再來看一下setOnNext( )方法,返回值是Action1,而里面的代碼我就直接return了一個Action1,但是我把call(Object o)用一個抽象方法分出去了,這么做有一個好處,就是代碼簡潔一些。這里面可能有一點繞,大家還是得多看看。那這個方法到底是干嘛用的???肯定有用啊,不然寫出來干嘛!!!如果你這個時候調用一下RxBus.send方法,你會發現,doOnNext是會被執行的,而且參數o就是send方法傳過來的參數。這么搞的話,是不是比BroadcastReceiver簡單多了!如果你還沒明白,那我們來寫個例子吧。
這個BaseActivity是抽象的,所以我們再寫一個Activity來繼承它,看一下我是怎么寫的。
public class MainActivity extends BaseActivity {
private final String TAG = MainActivity.class.getSimpleName();
@Override
protected void doOnNext(Object o) {
//接收到全局廣播之后在這里處理相關的業務邏輯
Log.d(TAG, "訂閱者:" + TAG + "->接收到的消息:" + o.toString());
}
}
public class MainActivity2 extends BaseActivity {
private final String TAG = MainActivity2.class.getSimpleName();
@Override
protected void doOnNext(Object o) {
//接收到全局廣播之后在這里處理相關的業務邏輯
Log.d(TAG, "訂閱者:" + TAG + "->接收到的消息:" + o.toString());
}
}
我寫了兩個Activity,其他不相關的代碼我已經刪掉了,所以代碼看起來很簡潔。這兩個Activity都對doOnNext進行了處理。這個時候我們要定義一個按鈕,當點擊按鈕的時候,就發送一條信息,這個按鈕的點擊事件是這么寫的:
@Override
public void onClick(View v) {
RxBus.send(new Random().nextInt());
}
代碼很簡單,就是發送一個隨機的數字。現在萬事俱備,只欠東風,我們要讓程序跑起來。怎么跑呢?我是這么操作的:
public class MainActivity extends BaseActivity {
private final String TAG = MainActivity.class.getSimpleName();
@Override
protected void doOnNext(Object o) {
//接收到全局廣播之后在這里處理相關的業務邏輯
Log.d(TAG, "訂閱者:" + TAG + "->接收到的消息:" + o.toString());
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
……
……
……
//啟動MainActivity2
Intent intent = new Intent(this, MainActivity2.class);
startActivity(intent);
}
}
進入到MainActivity之后,立馬啟動MainActivity2,這樣就能保證兩個Activity同時存在了。我們這個時候再來看一下MainActivity2的代碼:
public class MainActivity2 extends BaseActivity {
private final String TAG = MainActivity.class.getSimpleName();
@Override
protected void doOnNext(Object o) {
//接收到全局廣播之后在這里處理相關的業務邏輯
Log.d(TAG, "訂閱者:" + TAG + "->接收到的消息:" + o.toString());
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
……
……
……
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
RxBus.send(new Random().nextInt());
}
});
}
}
代碼也很簡單,就是每次點擊按鈕的時候,都會發送一個隨機數字,只要是訂閱過的人,都可以接收到這條消息。我運行了一下程序,看一下運行結果:
114.png
可以看到,兩個訂閱者都收到消息了。那我們現在嘗試去更新一下UI,代碼要稍微改動一下,改成這樣:
public class MainActivity extends BaseActivity {
private TextView rxbus_tv;
@Override
protected void doOnNext(Object o) {
if (rxbus_tv != null) {
rxbus_tv.setText("接收到消息->" + o.toString());
}
}
@Override
protected void onCreate() {
……
……
……
rxbus_tv = (TextView) findViewById(R.id.activity_rxbus1_tv);
findViewById(R.id.activity_rxbus1_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, TestRxActivity2.class);
startActivity(intent);
}
});
}
}
public class MainActivity2 extends BaseActivity {
private TextView rxbus_tv;
@Override
protected void doOnNext(Object o) {
if (rxbus_tv != null) {
rxbus_tv.setText("接收到消息->" + o.toString());
}
}
@Override
protected void onCreate() {
……
……
……
rxbus_tv = (TextView) findViewById(R.id.activity_rxbus2_tv);
findViewById(R.id.activity_rxbus2_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RxBus.send(new Random().nextInt());
}
});
}
}
沒有太多的改動,就是在doOnNext方法里面把Log打印語句換成了setText。看一下運行效果:
rxbus.gif
每當我在MainActivity2中發送一條消息的時候,MainActivity1和MainActivity2都可以接收到并且能夠去更新UI,是不是感覺很方便。
那么這個時候有的同學就要問了,我只想讓MainActivity1去接收我的消息并且更新UI,我不想讓MainActivity2去更新,怎么辦呢?
其實這個問題很好解決的。我們只需要在相應的Activity中加一些過濾條件就好了,問題是,怎么加過濾條件呢?
這里我先封裝一個bean對象,包含三個屬性,一個是from,一個是to,還有一個是content,具體代碼是這樣的:
public class RxBusBean {
private String from;
private String to;
private String content;
public RxBusBean() {
}
public RxBusBean(String from, String to, String content) {
this.from = from;
this.to = to;
this.content = content;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
其實理解起來就像寄快遞一樣,有寄貨方姓名,收貨方姓名和具體內容。
使用的時候可以封裝成Gson,這樣比較簡單。發送消息的時候代碼要改成這樣:
RxBusBean bean = new RxBusBean();
bean.setFrom("MainActivity2");
bean.setTo("MainActivity1");
bean.setContent(new Random().nextInt() + "");
Gson gson = new Gson();
RxBus.send(gson.toJson(bean));
接收消息的時候要改成這樣:
Gson gson = new Gson();
RxBusBean bean = gson.fromJson(o.toString(), RxBusBean.class);
if (bean.getTo().equals("MainActivity1")) {
rxbus_tv.setText("接收到消息->" + bean.getContent());
}
這樣處理之后,只有MainActivity1可以對消息進行處理。雖然MainActivity2可以接收到消息,但是沒辦法對消息進行處理。看一下效果圖:
看到沒有,已經達到我們想要的效果了。:smile::smile::smile:
來自:http://www.jianshu.com/p/ebaa10b18963