項目到了一定階段會出現(xiàn)一種甜蜜的負擔:業(yè)務的不斷發(fā)展與人員的流動性越來越大,代碼維護與測試回歸流程越來越繁瑣。這個時候需要對項目進行兩方面的重構:
1.分層操作,方便復用
2.模塊解耦,減少影響
小英團隊經過多次調研之后有如下成果:
分層操作
下圖是項目的分層模型:
ProgectUI:界面展示層,包括各個Activity,F(xiàn)ragment頁面,以及相對復雜的一些UI組件等
Bussiness:實際業(yè)務層,比如:用戶點擊登錄按鈕,去執(zhí)行登錄的操作
BussinessService:業(yè)務服務層,對外提供數(shù)據(jù)服務。比如:用戶信息模塊對外提供用戶相關的所有信息
Interface層:網(wǎng)絡請求與數(shù)據(jù)緩存層;將用戶的網(wǎng)絡接口單獨作為一層,并根據(jù)實際需要設置是否進行緩存。
DbCache層:數(shù)據(jù)庫與數(shù)據(jù)模型轉換層;所有數(shù)據(jù)庫操作都使用DbCache;
CoreService層:功能同F(xiàn)rameWork層,但是較重,故拆出;CoreService與FrameWork層具備業(yè)務無關性、通用性;主要有:分發(fā)器,Hybrid,熱修復以及埋點等
FrameWork層:基礎的技術組件(網(wǎng)絡庫,圖片庫等)、三方服務封裝以及通用UI等;
以用戶信息模塊為例,介紹一下具體的實現(xiàn)過程:
ProgectUI對應NewLoginActivity:界面展示層,登錄頁面,通過ChrLoginView與業(yè)務邏輯解耦
Bussiness對應QuickLoginPresenter:實際業(yè)務層,用戶注冊、登錄、忘記密碼等操作;依賴View與Module,處理實際的業(yè)務邏輯
BussinessService對應UserInstance:業(yè)務服務層.存儲賬號信息與用戶信息。通過實現(xiàn)接口UserInfoInterface,向外提供用戶信息服務。
Interface對應ApiService:登錄相關的后臺接口。
FrameWork對應ApiUtils:封裝網(wǎng)絡庫Retrofit,代理網(wǎng)絡請求
模塊解耦
總體采用依賴注入的方式將服務的實現(xiàn)與使用分離:
UserInstance維護用戶模塊所有的信息,并通過UserInfoInterface與其他模塊進行隔離
ServiceManager負責維護各個模塊服務的注冊并提供訪問的接口
UserBean實體bean作為數(shù)據(jù)通信的格式,負責統(tǒng)計用戶模塊所有的信息
EventBus消息總線負責向外提供本模塊的方法調用
總體思想:依賴注入負責對外暴露數(shù)據(jù);EventBus負責對外暴露回調方法。
接下來就來介紹本文的主體:EventBus
簡單介紹
EventBus-Android端事件發(fā)布/訂閱框架,特點如下:
簡化組件之間的通信。Android常用的通信方式:Broadcast、Listener、靜態(tài)變量以及通過Handler進行線程之間的通信等。都可以統(tǒng)一使用EventBus。
簡化代碼。不同于使用Listener通信方式,層層傳遞,模塊之間耦合嚴重。EventBus使用非常簡單并且無耦合
高性能。框架中采用緩存、池化技術、細粒度鎖、索引加速等方式使開發(fā)者無需關注安全性能方面的問題
依賴包不足50k
高級屬性:線程模型、優(yōu)先級以及是否接收粘性事件等
總線的工作機制如圖:
訂閱者通過EventBus訂閱相關事件,并準備好回調方法
發(fā)布者將事件發(fā)送給post出去,EventBus負責通知訂閱者
極簡使用
分為五步:導入依賴、初始化總線、定義事件、注冊訂閱者、發(fā)送事件
導入依賴
項目中Module的build.gradle中添加依賴:
compile 'org.greenrobot:eventbus:3.0.0’
如果不需要索引加速,直接跳到第二步。
索引加速使用到編譯時注解,所以需要在項目gradle中添加apt編譯插件:
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8’
在Module中申請apt插件生成索引的完整類名
apply plugin: 'com.neenbedankt.android-apt'
apt {
arguments {
eventBusIndex “com.chinahrMyEventBusIndex"
}
}
Module中引入EventBusAnnotationProcessor:
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1’
編譯項目之后,就可以在\app\build\generate\source\apt下看到生成的索引類
初始化總線
EventBus可以通過getDefault()方法獲取單例
EventBus.getDefault().register(this);
也可以通過EventBus.builder()去構造自定義的EventBus。另外還可以通過Bulder.installDefaultEventBus()修改默認的單例
EventBus.builder().eventInheritance(true).installDefaultEventBus();
如果需要索引加速,將編譯時生成的Index通過Builder添加進去
EventBus.getDefault().builder().addIndex(new MyEventBusIndex());
定義事件
所有可以被實例化成object的類都可以作為事件
public class MyEvent {
}
注冊訂閱者
訂閱事件的類中執(zhí)行register()
EventBus.getDefault().register(this);
并在監(jiān)聽事件的回調方法上添加注解@SubScribe,可配置屬性:方法執(zhí)行的線程模型,分發(fā)的優(yōu)先級,是否接收粘性事件
@Subscribe(threadMode = ThreadMode.MAIN,priority = 0,sticky = true)
public ?void onEventLogin(MyEvent myEvent){
Toast.makeText(MainActivity.this,"登錄成功",Toast.LENGTH_LONG).show();
}
為了防止內存泄露,在總線中對訂閱者進行注銷,比如在Activity的OnDestroy()中:
EventBus.getDefault().unregister(this);
發(fā)送事件
調用post(myEvent)或postSticky(myEvent)
EventBus.getDefault().post(new MyEvent());
以上完成了消息訂閱/發(fā)布的整個流程。
接下來將會說明框架內部的結構:
整體框架
四部分組成,如圖:
數(shù)據(jù)元素
框架主要涉及這些數(shù)據(jù)元素:訂閱者Subscriber,方法主體Method,事件event,事件類型eventType,線程模型ThreadMode,方法優(yōu)先級priority,是否接收粘性事件sticky
SubscriberMethod與訂閱者subscriber組合成Subscription,即訂閱方法。
一個方法的執(zhí)行需要方法主體(Method),調用方法的對象(Subscriber),參數(shù)(事件event)
post(event)之后結合Subscription就可以完成Method的調用。
EventBus
框架的門面,維護三個Map,并負責分發(fā)事件與執(zhí)行訂閱方法。
Map, CopyOnWriteArrayList>subscriptionsByEventType.事件類型與訂閱方法列表的對應關系。注冊事件,發(fā)送事件都是在操作此map.
Map>>typesBySubscriber.訂閱者與訂閱事件類型的對應關系。為了方便注銷訂閱者。
Map, Object>stickyEvents.粘性事件列表
調度器
回調方法通過四種方式進行分發(fā),即線程模型ThreadMode:
POSTING:默認模式,直接在當前線程執(zhí)行
MAIN:如果當前是主線程,直接執(zhí)行;如果不是主線程,通過handler發(fā)送到主線程執(zhí)行。
BACKGROUND:如果是主線程,交給backgroundPoster去調度;如果不是主線程就直接執(zhí)行。
ASYN:交給asyncPoster調度。asyncPoster會直接在線程池當中開啟一個線程執(zhí)行。
索引加速
編譯時,apt插件通過EventBusAnnotationProcessor提取注解@Subscribe生成索引MyEventBusIndex.索引內部維護一個Map,輔助EventBus查找方法信息
Map, SubscriberInfo>SUBSCRIBER_INDEX.訂閱者的類型與回調方法列表對應關系
整個流程一句話總結:在訂閱者準備好處理事件的回調方法之后,EventBus根據(jù)訂閱者對象經過反射或者索引加速獲取回調方法的信息,接收發(fā)布的事件event,按照指定的線程模型執(zhí)行回調方法。
EventBus內部設計十分精致,對于編程技能的提高有非常大的幫助。接下來介紹:
設計思想
作為一個框架主要有四方面的設計:門面、調度器、線程安全、性能調優(yōu)等
門面
1.EventBus類,門面模式中的門面類對外提供集中化和簡化的溝通管道。總線的所有操作(注冊訂閱者,發(fā)送事件,調度執(zhí)行,注銷訂閱者等)被封裝到EventBus中,有效地屏蔽實現(xiàn)細節(jié),使用和維護起來非常方便。
public classEventBus {
/**
* Registers the given subscriber to receive events. Subscribers must call {@link#unregister(Object)} once they
* are no longer interested in receiving events.
*
* Subscribers have event handling methods that must be annotated by {@linkSubscribe}.
* The {@linkSubscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {Class subscriberClass = subscriber.getClass();
List subscriberMethods =subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized(this) {
for(SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
/** Posts the given event to the event bus. */
public voidpost(Object event) {
PostingThreadState postingState =currentPostingThreadState.get();
List eventQueue = postingState.eventQueue;
eventQueue.add(event);
if(!postingState.isPosting) {
postingState.isMainThread= Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting=true;
if(postingState.canceled) {
throw newEventBusException("Internal error. Abort state was not reset");
}
try{
while(!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
}finally{
postingState.isPosting=false;
postingState.isMainThread=false;
}
}
}
voidinvokeSubscriber(Subscription subscription, Object event) {
try{
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
}catch(InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
}catch(IllegalAccessException e) {
throw newIllegalStateException("Unexpected exception", e);
}
}
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {List> subscribedTypes =typesBySubscriber.get(subscriber);
if(subscribedTypes !=null) {
for(Class eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
}else{
Log.w(TAG,"Subscriber to unregister was not registered before: "+ subscriber.getClass());
}
}
}
2.EventBus對象的構建,采用volatile實例與雙重檢查加鎖方式保證線程安全。
static volatileEventBusdefaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public staticEventBus getDefault() {
if(defaultInstance==null) {
synchronized(EventBus.class) {
if(defaultInstance==null) {
defaultInstance=newEventBus();
}
}
}
returndefaultInstance;
}
3.采用Builder模式輔助構建實例,避免因構造參數(shù)多帶來的構造器繁多,即避免重復重疊構造器模式;也避免了采用javaBean封裝參數(shù)帶來的修改一致性問題。
public classEventBus {
private static finalEventBusBuilderDEFAULT_BUILDER=newEventBusBuilder();
EventBus(EventBusBuilder builder) {
subscriptionsByEventType=newHashMap<>();
typesBySubscriber=newHashMap<>();
stickyEvents=newConcurrentHashMap<>();
mainThreadPoster=newHandlerPoster(this, Looper.getMainLooper(),10);
backgroundPoster=newBackgroundPoster(this);
asyncPoster=newAsyncPoster(this);
indexCount= builder.subscriberInfoIndexes!=null? builder.subscriberInfoIndexes.size() :0;
subscriberMethodFinder=newSubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions= builder.logSubscriberExceptions;
logNoSubscriberMessages= builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent= builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent= builder.sendNoSubscriberEvent;
throwSubscriberException= builder.throwSubscriberException;
eventInheritance= builder.eventInheritance;
executorService= builder.executorService;
}
public staticEventBusBuilder builder() {
return newEventBusBuilder();
}
}
public classEventBusBuilder {
/** Default: true */
publicEventBusBuilder logSubscriberExceptions(booleanlogSubscriberExceptions) {
this.logSubscriberExceptions= logSubscriberExceptions;
return this;
}
/** Builds an EventBus based on the current configuration. */
publicEventBus build() {
return newEventBus(this);
}
}
4.構造方法采用public,可以構建在項目中對不同的子模塊創(chuàng)建消息總線。Builder中提供修改默認單例的方法installDefaultEventBus(),是全局單例更加靈活。
public classEventBus {
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link#getDefault()}.
*/
publicEventBus() {
this(DEFAULT_BUILDER);
}
}
public classEventBusBuilder {
* Installs the default EventBus returned by {@linkEventBus#getDefault()} using this builders' values. Must be
* done only once before the first usage of the default EventBus.
*
*@throwsEventBusException if there's already a default EventBus instance in place
*/
publicEventBus installDefaultEventBus() {
synchronized(EventBus.class) {
if(EventBus.defaultInstance!=null) {
throw newEventBusException("Default instance already exists."+
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance= build();returnEventBus.defaultInstance;
}
}
}
總結一句話:EvntBus作為框架的門面,采用雙檢鎖,builder模式,構造參數(shù)public,封裝流程細節(jié),使得總線可以靈活配置與統(tǒng)一管理。
數(shù)據(jù)結構Map化
利用對象的class屬性作為Map的key值可以使代碼更加簡潔。這也是只需要傳入一個對象就可以驅動整個流程運行起來的關鍵。
private finalMap, CopyOnWriteArrayList>subscriptionsByEventType;
private finalMap>>typesBySubscriber;
private finalMap, Object>stickyEvents;
// Must be called in synchronized block
private voidsubscribe(Object subscriber, SubscriberMethod subscriberMethod) {
…………
…………
…………
if(subscriberMethod.sticky) {
if(eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List).
Set, Object>> entries =stickyEvents.entrySet();
for(Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey();
if(eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}else{
Object stickyEvent =stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
public voidpostSticky(Object event) {
synchronized(stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
這個可以借鑒到組件化設計當中的服務管理器ServiceManager,維護一個以服務接口類為key,實現(xiàn)類作為value的Map。簡化注冊調用方法。
public classServiceManager {
Map serviceMap=new HashMap<>();static volatileServiceManagerdefaultInstance;
public staticServiceManager ?getInstance(){
if(defaultInstance==null){
synchronized(ServiceManager.class){
if(defaultInstance==null){
defaultInstance=newServiceManager();
}
}
}
returndefaultInstance;
}
public voidregister(Object serviceInstance){
serviceMap.put(serviceInstance.getClass().getInterfaces()[0],serviceInstance);
}
publicTgetService(Class serviceType){
return(T)serviceMap.get(serviceType);
}
}
public interfaceUserinfoInterface {
publicUserBean getUserInfo();
}
注冊服務
public classUserInstanceimplementsUserinfoInterface {
UserInstance(){
ServiceManager.getInstance().register(this);
}
@Override
publicUserBean getUserInfo() {
return newUserBean();
}
}
其他模塊調用用戶信息:
public classResumeInstance {
voiddoSomeThing(){
UserinfoInterface userinfoInterface=ServiceManager.getInstance().getService(UserinfoInterface.class);
if(userinfoInterface!=null)
userinfoInterface.getUserInfo();
}
}
調度器
Android的線程有個特點:主線程不能被阻塞,UI的更新位于主線程,耗時操作如網(wǎng)絡處理在后臺線程
框架采用下面的方式進行調度:
每個調度器都維護一個待處理方法隊列PendingPostQueue。值得一提的是:poll(intmaxMillisToWait)出隊時如果當前隊列為空,會釋放當前對象的鎖,等待隊列填充。這個功能將在下面性能分析時解釋
final classPendingPostQueue {
privatePendingPosthead;
privatePendingPosttail;
synchronized voidenqueue(PendingPost pendingPost) {
if(pendingPost ==null) {
throw newNullPointerException("null cannot be enqueued");
}
if(tail!=null) {
tail.next= pendingPost;
tail= pendingPost;
}else if(head==null) {
head=tail= pendingPost;
}else{
throw newIllegalStateException("Head present, but no tail");
}
notifyAll();
}
synchronizedPendingPost poll() {
PendingPost pendingPost =head;
if(head!=null) {
head=head.next;
if(head==null) {
tail=null;
}
}
returnpendingPost;
}
synchronizedPendingPost poll(intmaxMillisToWait)throwsInterruptedException {
if(head==null) {
wait(maxMillisToWait);
}
returnpoll();
}
}
HandlerPoster負責在主線程中處理事件,顯然它是Handler的子類
final classHandlerPosterextendsHandler {
private finalPendingPostQueuequeue;
private final intmaxMillisInsideHandleMessage;
private finalEventBuseventBus;
private booleanhandlerActive;
HandlerPoster(EventBus eventBus, Looper looper,intmaxMillisInsideHandleMessage) {
super(looper);
this.eventBus= eventBus;
this.maxMillisInsideHandleMessage= maxMillisInsideHandleMessage;
queue=newPendingPostQueue();
}
voidenqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized(this) {
queue.enqueue(pendingPost);
if(!handlerActive) {
handlerActive=true;
if(!sendMessage(obtainMessage())) {
throw newEventBusException("Could not send handler message");
}
}
}
}
@Override
public voidhandleMessage(Message msg) {
booleanrescheduled =false;
try{
longstarted = SystemClock.uptimeMillis();
while(true) {
PendingPost pendingPost =queue.poll();
if(pendingPost ==null) {
synchronized(this) {
// Check again, this time in synchronized
pendingPost =queue.poll();
if(pendingPost ==null) {
handlerActive=false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
longtimeInMethod = SystemClock.uptimeMillis() - started;
if(timeInMethod >=maxMillisInsideHandleMessage) {
if(!sendMessage(obtainMessage())) {
throw newEventBusException("Could not send handler message");
}
rescheduled =true;
return;
}
}
}finally{
handlerActive= rescheduled;
}
}
}
BackgroundPoster繼承自Runnable,某一時段內待處理都會在BackgroundPoster的run方法中排隊處理。BackgroundPoster正在被線程池執(zhí)行時,executorRunning==true,此時發(fā)布的事件只會進隊列,不會再次調用線程池的execute方法。事件全部處理后才退出死循環(huán),設置executorRunning=fasle,此后再發(fā)布事件才會在線程池中開辟一個新線程。
上文提到(PendingPostQueue.poll(int))在隊列為空的時候等待隊列中添加元素。以及executorRunning標示位的使用,都是為了重用當前對象。
final classBackgroundPosterimplementsRunnable {
private finalPendingPostQueuequeue;
private finalEventBuseventBus;
private volatile booleanexecutorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus= eventBus;
queue=newPendingPostQueue();
}
public voidenqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized(this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public voidrun() {
try{
try{
while(true) {
PendingPost pendingPost = queue.poll(1000);
if(pendingPost ==null) {
synchronized(this) {
// Check again, this time in synchronized
pendingPost =queue.poll();
if(pendingPost ==null) {
executorRunning=false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
}catch(InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() +" was interruppted", e);
}
}finally{
executorRunning=false;
}
}
}
AyncPoster很簡單,來了事件就在線程池中開辟線程執(zhí)行。
classAsyncPosterimplementsRunnable {
private finalPendingPostQueuequeue;
private finalEventBuseventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus= eventBus;
queue=newPendingPostQueue();
}
public voidenqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public voidrun() {
PendingPost pendingPost =queue.poll();
if(pendingPost ==null) {
throw newIllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
總體一句話:三種方式的線程模型與一個方法鏈表組成一個非常標準Android版線程調度器。
線程安全
框架在應對多線程方面做了很多設計
1.粒度鎖
public classEventBus {
private finalMap, CopyOnWriteArrayList>subscriptionsByEventType;
private finalMap>>typesBySubscriber;
private finalMap, Object>stickyEvents;
public voidregister(Object subscriber) {
Class subscriberClass = subscriber.getClass();
List subscriberMethods =subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
private booleanpostSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
…………
}
public booleanhasSubscriberForEvent(Class eventClass) {
List> eventTypes =lookupAllEventTypes(eventClass);
if(eventTypes !=null) {
intcountTypes = eventTypes.size();
for(inth =0; h < countTypes; h++) {
Class clazz = eventTypes.get(h);
CopyOnWriteArrayList subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(clazz);
}
if(subscriptions !=null&& !subscriptions.isEmpty()) {
return true;
}
}
}
return false;
}
}
只在使用到需要同步的數(shù)據(jù)結構時,才去synchronized(this),獲取當前對象鎖。另外,對于在部分流程才會使用的數(shù)據(jù)結構并不會占用當前對象鎖,比如:stickyEvents,但問題就出在這里,stickyEvents是ConcurrentHashMap線程安全的,并且粒度比當前對象小。
public classEventBus {
private finalMap, Object>stickyEvents;
public voidpostSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
publicTgetStickyEvent(Class eventType) {
synchronized (stickyEvents) {
return eventType.cast(stickyEvents.get(eventType));
}
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType=newHashMap<>();
typesBySubscriber=newHashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster=newHandlerPoster(this, Looper.getMainLooper(),10);
backgroundPoster=newBackgroundPoster(this);
asyncPoster=newAsyncPoster(this);
indexCount= builder.subscriberInfoIndexes!=null? builder.subscriberInfoIndexes.size() :0;
subscriberMethodFinder=newSubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions= builder.logSubscriberExceptions;
logNoSubscriberMessages= builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent= builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent= builder.sendNoSubscriberEvent;
throwSubscriberException= builder.throwSubscriberException;
eventInheritance= builder.eventInheritance;
executorService= builder.executorService;
}
2.數(shù)據(jù)結構的合理使用。
方法列表采用CopyOnWriteArrayList,CopyOnWrite容器即寫時復制的容器。通俗的理解是當往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。這樣做的好處是可以對CopyOnWrite容器進行并發(fā)的讀,而不需要加鎖,因為當前容器不會添加任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。缺點就是會造成內存占用雙份,以及數(shù)據(jù)一致性問題(CopyOnWrite容器只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實時一致性。)考慮到方法只會在注冊的時候進行添加,因此采用偏緩存性質的數(shù)據(jù)結構更適合于當前的應用場景。
線程狀態(tài)信息采用ThreadLocal類型進行保存,ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數(shù)據(jù)的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。
public classEventBus {
private finalMap,CopyOnWriteArrayList>subscriptionsByEventType;
private finalThreadLocalcurrentPostingThreadState=newThreadLocal() {
@Override
protectedPostingThreadState initialValue() {
return newPostingThreadState();
}
};
總結一句話:通過synchronized與ThreadLocal的數(shù)據(jù)類型,構建出線程安全以及簡潔優(yōu)美的代碼。
高性能
1.索引加速
總線注冊時只傳入通過注解標記回調方法的訂閱者對象。EventBus把獲取訂閱方法的過程放在編譯時,避免運行時反射帶來的性能問題
@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe")
@SupportedOptions(value = {"eventBusIndex", "verbose"})
public class EventBusAnnotationProcessor extends AbstractProcessor {
/** Found subscriber methods for a class (without superclasses). 被注解表示的方法信息 */
private final ListMap methodsByClass = new ListMap<>();
private final Set classesToSkip = new HashSet<>(); // checkHasErrors檢查出來的異常方法
@Override
public boolean process(Set annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
if (index == null) { // 如果沒有在gradle中配置apt的argument,編譯就會在這里報錯
messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX +
" passed to annotation processor");
return false;
}
/** ... */collectSubscribers(annotations, env, messager);// 根據(jù)注解拿到所有訂閱者的回調方法信息checkForSubscribersToSkip(messager, indexPackage);// 篩掉不符合規(guī)則的訂閱者
if (!methodsByClass.isEmpty()) {createInfoIndexFile(index);// 生成索引類
}
/** 打印錯誤 */
}
/** 下面這些方法就不再貼出具體實現(xiàn)了,我們了解它們的功能就行 */
private void collectSubscribers // 遍歷annotations,找出所有被注解標識的方法,以初始化methodsByClass
private boolean checkHasNoErrors // 過濾掉static,非public和參數(shù)大于1的方法
private void checkForSubscribersToSkip // 檢查methodsByClass中的各個類,是否存在非public的父類和方法參數(shù)
/** 下面這三個方法會把methodsByClass中的信息寫到相應的類中 */
private void writeCreateSubscriberMethods
private void createInfoIndexFile
private void writeIndexLines
}
獲取所有訂閱者的回調方法信息之后,生成Index。運行時就可直接使用Map去查找回調方法。
packagecom.example.wangbinlong.myapplication;
importorg.greenrobot.eventbus.meta.SimpleSubscriberInfo;
importorg.greenrobot.eventbus.meta.SubscriberMethodInfo;
importorg.greenrobot.eventbus.meta.SubscriberInfo;
importorg.greenrobot.eventbus.meta.SubscriberInfoIndex;
importorg.greenrobot.eventbus.ThreadMode;
importjava.util.HashMap;
importjava.util.Map;
/** This class is generated by EventBus, do not edit. */
public classMyEventBusIndeximplementsSubscriberInfoIndex {
private static final Map, SubscriberInfo>SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX= new HashMap, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onLogin", EventBusManager.LoginEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(SecondActivity.Inner.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onStickyEvent", EventBusManager.StickEvent.class, ThreadMode.MAIN, 0, true),
}));
}
private static voidputIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
publicSubscriberInfo getSubscriberInfo(Class subscriberClass) {
SubscriberInfo info =SUBSCRIBER_INDEX.get(subscriberClass);
if(info !=null) {
returninfo;
}else{
return null;
}
}
}
總結一句話:如果項目中通過反射可以獲取的對應關系,可以通過獲取編譯時注解的方式進行索引加速
2.池化技術
EventBus的設計非常精致,但是有一個明顯的缺陷:產生很多中間對象。為了最大限度地減少影響,項目中多處使用緩存,對象池。
METHOD_CACHE緩存訂閱者類型與回調方法的列表,避免重復查找
classSubscriberMethodFinder {
List findSubscriberMethods(Class subscriberClass) {
List subscriberMethods =METHOD_CACHE.get(subscriberClass);
if(subscriberMethods !=null) {
returnsubscriberMethods;
}
if(ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
}else{
subscriberMethods = findUsingInfo(subscriberClass);
}
if(subscriberMethods.isEmpty()) {
throw newEventBusException("Subscriber "+ subscriberClass
+" and its super classes have no public methods with the @Subscribe annotation");
}else{
METHOD_CACHE.put(subscriberClass, subscriberMethods);
returnsubscriberMethods;
}
}
}
private static finalMap, List>METHOD_CACHE=newConcurrentHashMap<>();
eventTypesCache緩存事件類型與(事件類型父類和接口)的關系
private static finalMap, List>>eventTypesCache=newHashMap<>();
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private staticList> lookupAllEventTypes(Class eventClass) {
synchronized(eventTypesCache) {
List> eventTypes =eventTypesCache.get(eventClass);
if(eventTypes ==null) {
eventTypes =newArrayList<>();
Class clazz = eventClass;
while(clazz !=null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
returneventTypes;
}
}
通過反射查找訂閱方法時,將所有中間對象封裝成FindState.全局維護一個FindState的對象池。使用完對象之后通過recycle()擦除痕跡。
classSubscriberMethodFinder {
privateList findUsingInfo(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while(findState.clazz!=null) {
findState.subscriberInfo= getSubscriberInfo(findState);
if(findState.subscriberInfo!=null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for(SubscriberMethod subscriberMethod : array) {
if(findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
}else{
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
returngetMethodsAndRelease(findState);
}
private static finalFindState[]FIND_STATE_POOL=newFindState[POOL_SIZE];
privateList getMethodsAndRelease(FindState findState) {
List subscriberMethods =newArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized(FIND_STATE_POOL) {
for(inti =0; i
if(FIND_STATE_POOL[i] ==null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
returnsubscriberMethods;
}
privateFindState prepareFindState() {
synchronized(FIND_STATE_POOL) {
for(inti = 0; i
FindState state =FIND_STATE_POOL[i];
if(state !=null) {
FIND_STATE_POOL[i] =null;
returnstate;
}
}
}
return newFindState();
}
static classFindState{
final List subscriberMethods = new ArrayList<>();
finalMapanyMethodByEventType=newHashMap<>();
finalMapsubscriberClassByMethodKey=newHashMap<>();
finalStringBuildermethodKeyBuilder=newStringBuilder(128);
ClasssubscriberClass;
Classclazz;
booleanskipSuperClasses;
SubscriberInfosubscriberInfo;
voidinitForSubscriber(Class subscriberClass) {
this.subscriberClass=clazz= subscriberClass;
skipSuperClasses=false;
subscriberInfo=null;
}
void recycle() {
subscriberMethods.clear();
anyMethodByEventType.clear();
subscriberClassByMethodKey.clear();
methodKeyBuilder.setLength(0);
subscriberClass = null;
clazz = null;
skipSuperClasses = false;
subscriberInfo = null;
}
}
回調方法隊列PendPostQueue中的元素PendingPost本身就是對象池。構造器為私有,從而只能通過obtainPendingPost從對象池獲取對象,對象池中有保存的對象則獲取對象(ArrayList尾部獲取),沒有就通過new創(chuàng)建。releasePendingPost則將使用后的對象歸還給對象池,歸還的時候要將對象的使用痕跡擦除,同時要限制對象池大小為10000,防止對象池無限增大。
final classPendingPost {
private final static ListpendingPostPool= new ArrayList();
Objectevent;
Subscriptionsubscription;
PendingPostnext;
privatePendingPost(Object event, Subscription subscription) {
this.event= event;
this.subscription= subscription;
}
staticPendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized(pendingPostPool) {
intsize =pendingPostPool.size();
if(size >0) {
PendingPost pendingPost =pendingPostPool.remove(size -1);
pendingPost.event= event;
pendingPost.subscription= subscription;
pendingPost.next=null;
returnpendingPost;
}
}
return newPendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
總結一句話:通過編譯時注解解決反射問題,緩存與對象池解決內存開銷問題。
EventBus是通過可配置的門面來維護事件,訂閱者與回調方法之間的關系。然后按照指定線程模型調度執(zhí)行。的使用簡潔,線程安全,高性能的消息總線。最重要的是,它帶給大家一些編程上面的思考。