這也許是 RxBus 最友好的文章

open_ 7年前發布 | 12K 次閱讀 Gson RxJava 設計模式 Android開發 移動開發

本人之前做過一款社交類軟件,需要讓“未讀消息數”實時顯示到應用程序內(類似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

 

 本文由用戶 open_ 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!