Android 打造RxBus2.x的全面詳解

DeMonnnnnn發表於2018-09-08

前言

Android EventBus3.x的使用詳解一文中我們全面講解了使用EvenBus3.x解決程式/介面通訊。
本文將介紹另一個東西—>使用RxBus來實現程式/介面通訊。
閱讀本文,你需要提前瞭解RxJava,可以檢視:
給 Android 開發者的 RxJava 詳解
Android響應式程式設計框架—RxJava&RxAndroid2.0使用筆記

RxBus簡介

什麼是RxBus?

RxBus 名字看起來像一個庫,但它並不是一個庫,而是一種模式,它的思想是使用RxJava來實現了EventBus ,而讓你不再需要使用 Otto 或者 GreenRobot 的 EventBus。------ 給 Android 開發者的 RxJava 詳解

RxBus2.x就是基於RxJava2.x封裝實現的類。

為什麼要使用RxBus?

如上所說,我們可以通過封裝RxJava實現EventBus。
隨著RxJava在Android專案中的普及, 我們完全可以使用RxBus代替EventBus,減少庫的引入,增加系統的穩定性。
EventBus雖然使用方便,但是在事件的生命週期的處理上需要我們利用訂閱者的生命週期去註冊和取消註冊,這個部分還是略有麻煩之處。
而我們可以結合使用RxLifecycle來配置,簡化這一步驟。
結合RxJava的強大能力和RxAndroid的程式排程,RxBus有更多更強大的功能。

Demo&程式碼

GitHub:https://github.com/DeMonLiu623/DeMon-RxBus

RxBus實現

Gradle

    implementation 'io.reactivex.rxjava2:rxjava:2.1.17' //RxJava
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' //RxAndroid

封裝RxJava實現RxBus

Subject有兩種用途:

  1. 做為observable向其他的observable傳送事件
  2. 做為observer接收其他的observable傳送的事件

我們要實現:事件匯流排、事件釋出者以及事件訂閱者。首先Subject既可以作為被觀察者傳送事件,也可以作為觀察者接收事件,而RxJava內部的響應式的支援實現了事件匯流排的功能。
可以使用PublishSubject.create().toSerialized();生成一個Subject物件。
如下程式碼就實現了最基本的RxBus。

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;
    }

    /**
     * 傳送事件
     */
    public void post(Object event) {
        mBus.onNext(event);
    }

    /**
     * 根據傳遞的 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;
    }

}

使用RxBus

事件實體
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;
    }
}
訂閱事件
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"));

與EventBus對比,除了訂閱事件的訊息處理,使用基本一樣。
至於EventBus的執行緒模型,我們完全可以使用RxJava 的執行緒控制Scheduler來實現,具體可以參考上面RxJava的使用兩篇文章。

RxBus記憶體洩漏

使用RxJava釋出一個訂閱後,當頁面被finish,此時訂閱邏輯還未完成,如果沒有及時取消訂閱,就會導致Activity/Fragment無法被回收,從而引發記憶體洩漏。
EventBus為了解決這個問題,要求我們根據訂閱者的生命週期註冊和取消註冊。所以在RxBus中我們也可以這樣操作。

簡單處理

使用一個CompositeDisposable儲存當前所有的訂閱,然後再onDestroy()中將其dispose()。

private CompositeDisposable compositeDisposable;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
compositeDisposable = new CompositeDisposable();
        RxBus.getInstance().toObservable(MsgEvent.class).subscribe(new Observer<MsgEvent>() {
            @Override
            public void onSubscribe(Disposable d) {
                compositeDisposable.add(d);
            }

            @Override
            public void onNext(MsgEvent msgEvent) {
                //事件處理
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unbinder.unbind();
        if (compositeDisposable!=null && !compositeDisposable.isDisposed()){
            compositeDisposable.dispose();
        }
    }

RxLifecycle

Rxlifecycle 是trello開發的用於解決RxJava引起的記憶體洩漏的開源框架。
GitHub地址:https://github.com/trello/RxLifecycle
該框架為了適應不同的場景,開發了不同的版本,具體的可以檢視GitHub文件:

// RxLifecycle基礎庫
implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.2'

// Android使用的庫,可以繫結特定的生命週期
//需繼承RxActvivty使用
implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.2'

// Android元件庫,裡面定義了例如RxAppCompatActivity、RxFragment之類的Android元件
// 須繼承RxAppCompatActivity、RxFragment使用
implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'

// 預先編寫的支援首選項片段,將其子類化為提供者
implementation 'com.trello.rxlifecycle2:rxlifecycle-components-preference:2.2.2'

// Android使用的庫,須繼承NaviActivity使用
implementation 'com.trello.rxlifecycle2:rxlifecycle-navi:2.2.2'

// 使用Android生命週期作為提供者
//無需繼承,任何有宣告週期的元件都可以直接使用
implementation 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle:2.2.2'

// Kotlin語法 
implementation 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.2.2'

//在Kotlin語法,使用Android生命週期 
implementation 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle-kotlin:2.2.2'

如果我們自己有BaseActivity,所以不能繼承RxActvivty,RxAppCompatActivity、RxFragment,NaviActivity。
為了保持程式碼的靈活性,我們使用:

// 使用Android生命週期作為提供者
implementation 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle:2.2.2'

修改RxBus,新增如下程式碼:
使用compose(this.bindToLifecycle())方法繫結Activity的生命週期,在onStart方法中繫結,在onStop方法被呼叫後就會解除繫結,以此類推。
如果在onPause/onStop方法中繫結,那麼就會在它的下一個生命週期方法(onStop/onDestory)被呼叫後解除繫結。

繫結 銷燬
onCreate onDestory
onStart onStop
onResum onPause
onPause onStop
onStop onDestory
 /**
     * 使用Rxlifecycle解決RxJava引起的記憶體洩漏
     */
    public <T> Observable<T> toObservable(LifecycleOwner owner, final Class<T> eventType) {
        LifecycleProvider<Lifecycle.Event> provider = AndroidLifecycle.createLifecycleProvider(owner);
        return mBus.ofType(eventType).compose(provider.<T>bindToLifecycle());
    }

訂閱事件時,新增上下文this即可繫結生命週期,自動取消訂閱:

RxBus.getInstance().toObservable(this,MsgEvent.class).subscribe(new Consumer<MsgEvent>() {
            @Override
            public void accept(MsgEvent msgEvent) throws Exception {
                //處理事件
            }
        });

RxBus的粘性事件

EventBus有粘性事件,RxBus也可以實現。
修改RxBus修改&新增如下程式碼:

private final Map<Class<?>, Object> mStickyEventMap;

    private RxBus() {
        mBus = PublishSubject.create().toSerialized();
        mStickyEventMap = new ConcurrentHashMap<>();
    }
  /**
     * 傳送一個新Sticky事件
     */
    public void postSticky(Object event) {
        synchronized (mStickyEventMap) {
            mStickyEventMap.put(event.getClass(), event);
        }
        post(event);
    }

    /**
     * 根據傳遞的 eventType 型別返回特定型別(eventType)的 被觀察者
     * 使用Rxlifecycle解決RxJava引起的記憶體洩漏
     */
    public <T> Observable<T> toObservableSticky(LifecycleOwner owner,final Class<T> eventType) {
        synchronized (mStickyEventMap) {
            LifecycleProvider<Lifecycle.Event> provider = AndroidLifecycle.createLifecycleProvider(owner);
            Observable<T> observable = mBus.ofType(eventType).compose(provider.<T>bindToLifecycle());
            final Object event = mStickyEventMap.get(eventType);

            if (event != null) {
                return observable.mergeWith(Observable.create(new ObservableOnSubscribe<T>() {
                    @Override
                    public void subscribe(ObservableEmitter<T> subscriber) throws Exception {
                        subscriber.onNext(eventType.cast(event));
                    }
                }));
            } else {
                return observable;
            }
        }
    }

    /**
     * 根據eventType獲取Sticky事件
     */
    public <T> T getStickyEvent(Class<T> eventType) {
        synchronized (mStickyEventMap) {
            return eventType.cast(mStickyEventMap.get(eventType));
        }
    }

    /**
     * 移除指定eventType的Sticky事件
     */
    public <T> T removeStickyEvent(Class<T> eventType) {
        synchronized (mStickyEventMap) {
            return eventType.cast(mStickyEventMap.remove(eventType));
        }
    }

    /**
     * 移除所有的Sticky事件
     */
    public void removeAllStickyEvents() {
        synchronized (mStickyEventMap) {
            mStickyEventMap.clear();
        }
    }

RxBus完整程式碼

public class RxBus {
    private volatile static RxBus mDefaultInstance;
    private final Subject<Object> mBus;

    private final Map<Class<?>, Object> mStickyEventMap;

    private RxBus() {
        mBus = PublishSubject.create().toSerialized();
        mStickyEventMap = new ConcurrentHashMap<>();
    }

    public static RxBus getInstance() {
        if (mDefaultInstance == null) {
            synchronized (RxBus.class) {
                if (mDefaultInstance == null) {
                    mDefaultInstance = new RxBus();
                }
            }
        }
        return mDefaultInstance;
    }

    /**
     * 傳送事件
     */
    public void post(Object event) {
        mBus.onNext(event);
    }

    /**
     * 使用Rxlifecycle解決RxJava引起的記憶體洩漏
     */
    public <T> Observable<T> toObservable(LifecycleOwner owner, final Class<T> eventType) {
        LifecycleProvider<Lifecycle.Event> provider = AndroidLifecycle.createLifecycleProvider(owner);
        return mBus.ofType(eventType).compose(provider.<T>bindToLifecycle());
    }

    /**
     * 判斷是否有訂閱者
     */
    public boolean hasObservers() {
        return mBus.hasObservers();
    }

    public void reset() {
        mDefaultInstance = null;
    }


    /**
     * Stciky 相關
     */

    /**
     * 傳送一個新Sticky事件
     */
    public void postSticky(Object event) {
        synchronized (mStickyEventMap) {
            mStickyEventMap.put(event.getClass(), event);
        }
        post(event);
    }

    /**
     * 根據傳遞的 eventType 型別返回特定型別(eventType)的 被觀察者
     * 使用Rxlifecycle解決RxJava引起的記憶體洩漏
     */
    public <T> Observable<T> toObservableSticky(LifecycleOwner owner,final Class<T> eventType) {
        synchronized (mStickyEventMap) {
            LifecycleProvider<Lifecycle.Event> provider = AndroidLifecycle.createLifecycleProvider(owner);
            Observable<T> observable = mBus.ofType(eventType).compose(provider.<T>bindToLifecycle());
            final Object event = mStickyEventMap.get(eventType);

            if (event != null) {
                return observable.mergeWith(Observable.create(new ObservableOnSubscribe<T>() {
                    @Override
                    public void subscribe(ObservableEmitter<T> subscriber) throws Exception {
                        subscriber.onNext(eventType.cast(event));
                    }
                }));
            } else {
                return observable;
            }
        }
    }

    /**
     * 根據eventType獲取Sticky事件
     */
    public <T> T getStickyEvent(Class<T> eventType) {
        synchronized (mStickyEventMap) {
            return eventType.cast(mStickyEventMap.get(eventType));
        }
    }

    /**
     * 移除指定eventType的Sticky事件
     */
    public <T> T removeStickyEvent(Class<T> eventType) {
        synchronized (mStickyEventMap) {
            return eventType.cast(mStickyEventMap.remove(eventType));
        }
    }

    /**
     * 移除所有的Sticky事件
     */
    public void removeAllStickyEvents() {
        synchronized (mStickyEventMap) {
            mStickyEventMap.clear();
        }
    }

}

Demo

該Demo演示了使用RxBus完成Activty與Fragment的通訊。
這裡寫圖片描述這裡寫圖片描述

public class RxActivity extends AppCompatActivity {

    @BindView(R.id.java)
    Button java;
    @BindView(R.id.android)
    Button android;
    @BindView(R.id.rx_layout)
    FrameLayout rxLayout;
    private FragmentManager fragmentManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rx);
        ButterKnife.bind(this);
        fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.rx_layout, new RxFragment());
        transaction.commit();
    }

    @OnClick({R.id.java, R.id.android})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.java:
                RxBus.getInstance().post(new MsgEvent("Java"));
                break;
            case R.id.android:
                RxBus.getInstance().post(new MsgEvent("Android"));
                break;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/java"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Java" />

        <Button
            android:id="@+id/android"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Android" />
    </LinearLayout>

    <FrameLayout
        android:id="@+id/rx_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
public class RxFragment extends Fragment {
    @BindView(R.id.text)
    TextView text;
    Unbinder unbinder;
    private View view;
    @SuppressLint("CheckResult")
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_rx, container, false);
        unbinder = ButterKnife.bind(this, view);
    
        RxBus.getInstance().toObservable(this,MsgEvent.class).subscribe(new Consumer<MsgEvent>() {
            @Override
            public void accept(MsgEvent msgEvent) throws Exception {
                //處理事件
            }
        });
        return view;
    }

}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:text="RxFragment"
    android:textSize="18sp" />

<TextView
    android:id="@+id/text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:textSize="16sp" />
</LinearLayout>

參考

RxBus 簡單使用學習
https://www.2cto.com/kf/201708/674041.html

相關文章