android平臺中,EventBus研究學習

當一個Android應用功能越來越多的時候,app中各個部分之間通信,往往采用Observer的方式來進行,即註冊—-通知—-註銷的方式執行 各類控件經常需要根據某個狀態來更新顯示內容。這種場景常見的解決方式就是定義一個接口,需要關註該事件的控件來實現這個接口。 接口類:
public interface OnChangedListener {
void onDataChanged();
}
被觀察者往往以如下形式實現:

public abstract class AbsHTTPRequest {
    private final WeakHashMap mListeners = new WeakHashMap();
    public interface OnChangedListener {
        void onDataChanged();
    }
    
    /*HTTP's response*/
    public abstract void onResponse();
    
    public final void addListener(OnChangedListener listener) {
        mListeners.put(listener, true);
    }

    public final void removeListener(OnChangedListener listener) {
        mListeners.remove(listener);
    }
    
    protected final void notifyDataChanged() {
        Set keys = mListeners.keySet();
        if(keys != null) {
            Iterator iterator = keys.iterator();
            while(iterator.hasNext()) {
                iterator.next().onDataChanged();
            }
        }
    }
}

具體的主題角色( 被觀察者),實現方式如下:

public class LoginRequest extends AbsHTTPRequest implements OnChangedListener{
	public void onResponse(){
		addListener(this);
		notifyDataChanged();
	}

	@Override
	public void onDataChanged() {
		// TODO Auto-generated method stub
		System.out.println("LoginRequest");
	}
}

使用觀察者模式有一個弊病就是部件之間的耦合度太高,所有的主題角色都需要實現同一個interface。觀察者模式定義瞭一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。如果主題角色被註冊的observer越多,那麼需要實現的interface也就越多,接口方法數量也就越多。
如何來進行解耦,讓代碼邏輯更清晰,可讀性更強,是一個問題。
在Android中也有一個類似功能的開源庫EventBus,可以很方便的幫助我們實現觀察者模式,並且讓各個組件之間的耦合性更低。

關於EventBus的講解文章,網絡業很多,這裡推薦一篇,講解比較詳細的blog,https://www.cnblogs.com/angeldevil/p/3715934.html
對EventBus的認識,最好還是從demo入手,先易後難。首先知道如何使用,然後再深究源碼,才能循序漸進,吃透其中的設計理念,便於日後的代碼調試和模塊重構。關於demo,網上有很多,可以自己去查收。
EventBus的使用有4個步奏:
1.定義事件類型:
public class MyEvent
2.註冊訂閱者:
EventBus.getDefault().register(this)
3.發送事件:
EventBus.getDefault().post(new MyEvent())
4.接收事件,處理
訂閱者回調的函數。官方指導,函數名稱以onEvent開頭。

在EventBus模塊中,有幾個重要的概念,瞭解瞭這幾個概念後,也就不難懂瞭。

Event:可以是任意類型的對象
Subscriber:訂閱者,接收特定的
Publisher:發佈者,用於通知Subscriber發送

EventType:onEvent函數中的參數,表示事件對象,用戶自定義的。
Subscriber:訂閱源,即調用register註冊的對象,這個對象內包含onEvent成員函數。

SubscribMethod.java
final class SubscriberMethod {
    final Method method;  /*Method類型的method成員表示這個onEvent,即事件處理函數。同時也包含訂閱源*/
    final ThreadMode threadMode;
    final Class eventType; /*事件的對象,用戶自定義Object*/
... ... ... ... ... ... ... ... ... ... ... ...
}

Subscription.java

final class Subscription {
    final Object subscriber;  /*訂閱源Subscriber,即調用register註冊的對象*/
    final SubscriberMethod subscriberMethod; /**/
    final int priority;
... ... ... ... ... ... ... ... ... ... ... ...
}

類EventBus中,有兩個核心的成員

   /*EventType -> List,事件到訂閱對象之間的映射*/
    private final Map<Class, CopyOnWriteArrayList> subscriptionsByEventType;
  /* Subscriber -> List,訂閱對象到它訂閱的的所有事件的映射關系*/
    private final Map<Object, List<Class>> typesBySubscriber;

註冊流程:在調用register函數時,EventBus類有多個重載的register函數,但是作者更傾向於使用register(this);含有 多個參數的register函數中,明確標註瞭@deprecated,原創作者不建議使用。從代碼:

    public void register(Object subscriber) {
        register(subscriber, DEFAULT_METHOD_NAME, false, 0);
    }

可以觀察到,所有重載的register函數,都調用到瞭

    private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {
        List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),methodName);
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

其中註冊函數register,默認參數DEFAULT_METHOD_NAME為函數名稱”onEvent”,在java放射機制中,所有的事件處理函數名稱 統一為“onEvent”,僅僅參數不一致。onEvent的參數為用戶自定義的對象。
註冊時,使用SubscriberMethodFinder的對象,調用到findSubscriberMethods方法,獲取到List。
數組對象Method[],調用getMethods()方法, 獲取的是類的所有共有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實現的所有public方法。這也是為啥,我們的onEvent函數,要定義為public方法的原因哦。

在findSubscriberMethods函數中,進行如此頻繁的遍歷,就是為瞭找到List。 每一個訂閱者,對應一個List,有多少onEvent函數,返回的List,就有多少個item。即查找訂閱源內的事件處理方法,同時還會查到它的父類中的事件處理方法,返回list,交給

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)

進行處理。
Event與Subscriber之間,是一對多的關系。即一個事件,可以被多個訂閱者關註。

Subscriber與Event之間,也是一對多的關系。即一個訂閱者,可以訂閱多個事件。
subscribe方法,也就是將上述的那樣的關系,進行理順,合理的建立map的映射關系,主要做瞭這樣幾件事件。
a.根據SubscriberMethod中的EventType類型,將Subscribtion對象存放在subscriptionsByEventType中。建立EventType與Subscription的映射,一個事件可以有多個訂閱者。
b.根據Subscriber將EventType存放在typesBySubscriber`中,建立Subscriber到EventType的映射,每個Subscriber可以訂閱多個事件。
c.如果是Sticky類型的訂閱者,直接向它發送上個保存的事件(如果有的話)。
通過Subscriber到EventType的映射,我們就可以很方便地使一個Subscriber取消接收事件,通過EventType到Sucscribtion的映射,可以方便地將相應的事件發送到它的每一個訂閱者。
與Observer不同的是,使用EventBus,不同的被觀察者,不需統一實現Observer中的interface方法,在上層代碼中,也不需要逐一進行notify機制。通過Map進行訂閱源與事件函數的對應關系,進行解耦,為其核心之處。
發送流程:
EventBus.getDefault().post(new EventType());參數為用戶自定義的對象。最為簡單的處理方式,實現事件發送。
當事件發送出去後,所有的訂閱者,是如何調用其事件方法的呢?這個就需要遍歷上文提到的subscriptionsByEventType的Map瞭。Post發送事件,入口為post函數:public void post(Object event),在postSingleEvent函數中個,有一個重要的處理函數:

    /** Finds all Class objects including super classes and interfaces. */
    private List<Class> findEventTypes(Class eventClass) {
        synchronized (eventTypesCache) {
            List<Class> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<Class>();
                Class clazz = eventClass;
                while (clazz != null) {
                    eventTypes.add(clazz);
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    clazz = clazz.getSuperclass();
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

其作用,就是把這個事件類的對象、實現的接口及父類的類對象存到一個List中返回,根據list中的eventTypes,遍歷subscriptionsByEventType,獲取訂閱源對象,進行逐一的調用事件函數。
這裡需要註意的是,當Post一個事件時,這個事件的父事件(事件類的父類事件)、接口事件也會被Post,所以如果訂閱者接收Object類型的事件,即包含onEvent(Object object)事件函數,那麼Subscriber就可以接收所有的事件。
通過本篇博文的瞭解,EventBus就是通過Map,建立訂閱源與事件函數的對應關系,進行解耦,來規避Observer的接口方法的多次、頻繁的定義。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。