RxBus-實現EventBus之post

wzgiceman發表於2016-12-22

背景

是否有這樣的糾結:

  • 在已經習慣了EventBus的用法後,轉戰RxBus使用方法的不一致,導致多餘的學習和使用成本

  • 已經使用rxjava和rxAndroid到你的專案中,但是專案中又同時存在eventbus;因為rx完全可以替換掉eventbus所以導致了過多引入第三方jar包的問題,對於有程式碼潔癖和瘦身需求的同學們來說簡直是一個噩耗;
    如何在最大基礎上修改我們已經存在的程式碼呢,那就是改造一個自己的rxbus,讓他使用起來和eventbus一模一樣,這樣我們只需要將eventbus改名成rxbus即可,其他程式碼都不需要修改!

廢話到此為止,開始我們的優化之路

效果

RxBus-實現EventBus之post
這裡寫圖片描述

工程目錄

RxBus-實現EventBus之post
這裡寫圖片描述

程式碼使用

  • 註冊-登出-接受事件

     /*接受事件*/
     @Subscribe(threadMode= ThreadMode.MAIN)
     public void event(EventChangeText changeText){
         tvChange.setText(changeText.getChangeText());
     }
    
     @Override
     protected void onStart() {
         super.onStart();
         /*註冊*/
         RxBus.getDefault().register(this);
     }
    
     @Override
     protected void onDestroy() {
         super.onDestroy();
         /*登出*/
         RxBus.getDefault().unRegister(this);
     }複製程式碼
  • 傳送訊息
    @Override
      public void onClick(View v) {
          switch (v.getId()){
              case  R.id.btn_change_text:
                  RxBus.getDefault().post(new EventChangeText("我修改了-Main"));
                  break;
          }
      }複製程式碼

用過EventBus的同學一眼就應該能看出,用法完全一模一樣

封裝原理

封裝註解

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
    int code() default -1;
    ThreadMode threadMode() default ThreadMode.CURRENT_THREAD;
}複製程式碼

暫時我們們先了解ThreadMode 引數,code引數的使用在結尾再給大家解釋(比eventbus新增的一個功能)
ThreadMode 指定接受訊息的處理所在的執行緒,我們這裡定義了四種情況

處理模式

public enum ThreadMode {

    /**
     * current thread
     */
    CURRENT_THREAD,

    /**
     * android main thread
     */
    MAIN,


    /**
     * new thread
     */
    NEW_THREAD,

    /**
     * io
     */
    IO

}複製程式碼

完全是rx中自帶的四種處理模式

處理資訊類

封裝處理過程中的相關資訊,模式,接收訊息物件,code,接受訊息型別

public class SubscriberMethod {
    public Method method;
    public ThreadMode threadMode;
    public Class<?> eventType;
    public Object subscriber;
    public int code;

    public SubscriberMethod(Object subscriber, Method method, Class<?> eventType, int code,ThreadMode threadMode) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.subscriber = subscriber;
        this.code = code;
    }


    /**
     * 呼叫方法
     * @param o 引數
     */
    public void invoke(Object o){
        try {
            method.invoke(subscriber, o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}複製程式碼

RxBus封裝

集合上面的類,開始我們的rxbus封裝

初始化單利物件

感興趣的同學可以檢視另一篇關於單利部落格Android-主Activity不一樣的單利模式

 private static volatile RxBus defaultInstance;
    public static RxBus getDefault() {
        RxBus rxBus = defaultInstance;
        if (defaultInstance == null) {
            synchronized (RxBus.class) {
                rxBus = defaultInstance;
                if (defaultInstance == null) {
                    rxBus = new RxBus();
                    defaultInstance = rxBus;
                }
            }
        }
        return rxBus;
    }複製程式碼

初始化變數集合

記錄註冊資訊和釋出訊息資訊以及自定義的方法集合


    private Map<Class, List<Subscription>> subscriptionsByEventType = new HashMap<>();


    private Map<Object, List<Class>> eventTypesBySubscriber = new HashMap<>();


    private Map<Class, List<SubscriberMethod>> subscriberMethodByEventType = new HashMap<>();複製程式碼

註冊監聽

register的時候獲取@Subscribe註解的方法的相關資訊儲存到map,post事件觸發的時候呼叫@Subscribe註解的方法並傳入引數.

    /**
     * 註冊
     *
     * @param subscriber 訂閱者
     */
    public void register(Object subscriber) {
        Class<?> subClass = subscriber.getClass();
        Method[] methods = subClass.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Subscribe.class)) {
                //獲得引數型別
                Class[] parameterType = method.getParameterTypes();
                //引數不為空 且引數個數為1
                if (parameterType != null && parameterType.length == 1) {

                    Class eventType = parameterType[0];

                    addEventTypeToMap(subscriber, eventType);
                    Subscribe sub = method.getAnnotation(Subscribe.class);
                    int code = sub.code();
                    ThreadMode threadMode = sub.threadMode();

                    SubscriberMethod subscriberMethod = new SubscriberMethod(subscriber, method, eventType, code, threadMode);
                    addSubscriberToMap(eventType, subscriberMethod);

                    addSubscriber(subscriberMethod);
                }
            }
        }
    }複製程式碼

登出監聽

unRegister的移除儲存的subscriber、subscriberMethod已經Subscription取消訂閱事件
一定要及時的銷燬,不然記憶體洩露


    /**
     * 取消註冊
     *
     * @param subscriber
     */
    public void unRegister(Object subscriber) {
        List<Class> subscribedTypes = eventTypesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unSubscribeByEventType(eventType);
                unSubscribeMethodByEventType(subscriber, eventType);
            }
            eventTypesBySubscriber.remove(subscriber);
        }
    }複製程式碼

post請求

觸發請求

  /**
     * 提供了一個新的事件,單一型別
     *
     * @param o 事件資料
     */
    public void post(Object o) {
        bus.onNext(o);
    }複製程式碼

呼叫rx處理回撥


    /**
     * 用RxJava新增訂閱者
     *
     * @param subscriberMethod
     */
    public void addSubscriber(final SubscriberMethod subscriberMethod) {
        Observable observable;
        if (subscriberMethod.code == -1) {
            observable = toObservable(subscriberMethod.eventType);
        } else {
            observable = toObservable(subscriberMethod.code, subscriberMethod.eventType);
        }

        Subscription subscription = postToObservable(observable, subscriberMethod)
                .subscribe(new Action1<Object>() {
                    @Override
                    public void call(Object o) {
                        callEvent(subscriberMethod.code, o);
                    }
                });
        addSubscriptionToMap(subscriberMethod.eventType, subscription);
    }複製程式碼

code-post請求,更加方便

在借鑑eventbus訊息處理的模式上,新加入code判斷方式,這樣可以更加快速的新增sub物件,不用一個訊息初始化一個類,而且可以同時區分一個訊息的不同處理方式

效果

RxBus-實現EventBus之post
這裡寫圖片描述

傳送訊息

 public void onClick(View v) {
        switch (v.getId()){
            case  R.id.btn_change_text:
                RxBus.getDefault().post(new EventChangeText("我修改了-Main"));
                break;
            case  R.id.btn_code_simple:
                RxBus.getDefault().post(0x1,"簡單的code訊息");
                break;
            case  R.id.btn_code_diffrent:
                RxBus.getDefault().post(0x1,new EventChangeText("code方式-我修改了-Main"));
                break;

        }
    }複製程式碼

接收訊息


    /*單一code接受處理*/
    @Subscribe(code = 0x1,threadMode= ThreadMode.MAIN)
    public void event(String changeText){
        tvChange.setText(changeText);
    }


    /*code 不同事件接受處理*/
    @Subscribe(code = 0x1,threadMode= ThreadMode.MAIN)
    public void eventCode(EventChangeText changeText){
        tvChange.setText(changeText.getChangeText());
    }


    /*常規接受事件*/
    @Subscribe(threadMode= ThreadMode.MAIN)
    public void event(EventChangeText changeText){
        tvChange.setText(changeText.getChangeText());
    }複製程式碼

看完以後估計大家都明白了使用方法,code實現的過車和ThreadMode實現原理一樣,在分發事件處理的時候,通過code的判斷達到這樣的目的結果

 /**
     * 回撥到訂閱者的方法中
     *
     * @param code   code
     * @param object obj
     */
    private void callEvent(int code, Object object) {
        Class eventClass = object.getClass();
        List<SubscriberMethod> methods = subscriberMethodByEventType.get(eventClass);
        if (methods != null && methods.size() > 0) {
            for (SubscriberMethod subscriberMethod : methods) {

                Subscribe sub = subscriberMethod.method.getAnnotation(Subscribe.class);
                int c = sub.code();
                if (c == code) {
                    subscriberMethod.invoke(object);
                }

            }
        }
    }複製程式碼

匯入

compile 'com.wzgiceman:RxBus:1.0.2'複製程式碼

推薦手動匯入到自己的工程中,避免多餘的第三方jar包匯入,只要你的工程中有對rx的支出,將檔案copy到工程下面即可:

RxBus-實現EventBus之post
這裡寫圖片描述

rx資源地址

    /*rx-android-java*/
    compile 'io.reactivex:rxjava:+'
    compile 'com.squareup.retrofit:adapter-rxjava:+'
    compile 'com.trello:rxlifecycle:+'
    compile 'com.trello:rxlifecycle-components:+'複製程式碼

演示工程中使用的是rx2,可自行替換你使用的版本


原始碼

GitHub原始碼下載地址-傳送門


建議

如果你有任何的問題和建議歡迎加入QQ群告訴我!

相關文章