解耦
BeeHive的核心主要有兩類對象匆绣,一類是Service對象冻押,第二類是Module對象苗踪。
Service解除依賴的方法是通過Protocol抽象來完成,在使用service對象的時候通過BHServiceManager來獲取實(shí)現(xiàn)Protocol的對象絮爷,從而完成方法調(diào)用,不會依賴具體的對象類型梨树。而對于Module解耦是通過事件來實(shí)現(xiàn)的,也可以認(rèn)為是觀察者模式岖寞。
Service和Module對象
Service對象實(shí)現(xiàn)了某個protocol的對象抡四,使用它需要先通過BHServiceManager進(jìn)行注冊,這種對象的聲明周期一般由調(diào)用方?jīng)Q定仗谆,一般來說比較短可能調(diào)用完成就釋放了指巡。還有一類是module類型,這種類型對象由BeeHive中的BHModuleManager對象進(jìn)行管理隶垮,module對象也需要在注冊之后才能使用藻雪,一般來說它的聲明周期會長一些,通常還會處理app相關(guān)的一些事件狸吞,同時也可以自定義事件和處理自定義事件勉耀。
BHModuleProtocol定義了所有被管理起來的module需要實(shí)現(xiàn)的一些方法,但是都是可選實(shí)現(xiàn)蹋偏。主要是module自己排序相關(guān)的level和priority便斥,剩下的主要是BeeHive中定義的一些app生命周期的事件處理方法。
BHServiceProtocol定義了提供服務(wù)的對象是否是單例威始,自定義Service基本上都是繼承自它枢纠。
而BeeHive中的其他對象,基本上都是為這兩種類型的對象服務(wù)的:
BHServiceManager
這個類是用來管理service和implemention之間的映射黎棠,implemention的初始化也通過它來完成晋渺。但是對implemention的緩存是放在BHContext中,這個類的實(shí)現(xiàn)也比較簡單脓斩。
看一下頭文件木西,主要的接口就是為了注冊一個service和創(chuàng)建service對象:
@interface BHServiceManager : NSObject
@property (nonatomic, assign) BOOL enableException;
+ (instancetype)sharedManager;
- (void)registerLocalServices;
- (void)registerService:(Protocol *)service implClass:(Class)implClass;
- (id)createService:(Protocol *)service;
- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName;
- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache;
- (id)getServiceInstanceFromServiceName:(NSString *)serviceName;
- (void)removeServiceWithServiceName:(NSString *)serviceName;
@end
BHModuleManager
所有的module的管理對象,提供了module相關(guān)的聲明周期方法: 加載俭厚,注冊户魏,注銷以及module自身相關(guān)的元數(shù)據(jù)信息,比如是否已經(jīng)初始化挪挤,module的level和prioirty信息(用來對module進(jìn)行排序)叼丑。除了module還會管理與module相關(guān)的事件。整體上看扛门,其實(shí)這個對象核心就是處理各種module的事件鸠信。
當(dāng)需要注冊一個module的時候會對module進(jìn)行初始化,同時會生成module相關(guān)的元數(shù)據(jù)信息存放到BHModuleInfos數(shù)組中论寨。module對象的維護(hù)是存放在BHModules中星立,并通過module Level和 module Priority 進(jìn)行排序爽茴。然后是注冊與module關(guān)聯(lián)的事件,這里的事件默認(rèn)主要是系統(tǒng)事件绰垂,類似前后臺的切換室奏,openUrl的事件等。最后一步是如果有必要(通過傳入的參數(shù)來控制) 觸發(fā)一些初始化事件劲装,比如setup,init等事件胧沫。事件的管理是通過BHSelectorByEvent字典實(shí)現(xiàn)的,它存放了event和selector的映射占业,通過event可以查詢selector绒怨,再對module進(jìn)行調(diào)用處理。
以下是BHModuleManager內(nèi)部主要的數(shù)據(jù)存儲的對象谦疾,所有的module南蹂,事件信息都通過它們進(jìn)行管理
1)BHModuleInfos里存儲了module對應(yīng)的類型信息,level和priority信息念恍,以及是否初始化信息六剥,它保證全局同類型的module只有一個存在。
2)BHModules里存儲了所有的module信息
3)BHModulesByEvent里存儲event和module的映射峰伙,主要是為了處理事件發(fā)生時找到所有對應(yīng)的module
4)BHSelectorByEvent存儲event和selector的映射仗考,通過事件來查找對應(yīng)的相應(yīng)函數(shù),進(jìn)行調(diào)用
Module的事件封裝以及參數(shù)傳遞
下面說一下module事件的定義以及封裝词爬,在處理事件的過程中秃嗜,參數(shù)以及上下文信息如何進(jìn)行封裝和傳遞的。
它通過枚舉定義了所有系統(tǒng)層的事件顿膨,比如前后臺切換锅锨,openUrl,app一些生命周期的事件等等恋沃。它本身是通過Int來進(jìn)行存儲的必搞,所以用戶可以通過int來定義自己的事件。BHModuleManager會將傳入的事件參數(shù)封裝到BHContext中囊咏,并且通過copy的方式傳給對應(yīng)的module恕洲。而BHContext本身就存儲了各種上下文信息,比如開發(fā)環(huán)境梅割,config信息等霜第。通過它module可以知道當(dāng)前所需要的一些環(huán)境信息。BHContext本身是單例户辞,對于module而言應(yīng)該是使用copy之后的對象泌类,避免因?yàn)殄e誤的改動導(dǎo)致全局的參數(shù)發(fā)生變化影響到其他的module。
Service和Module的區(qū)別, 為什么同時存在
Service用來創(chuàng)建一些實(shí)現(xiàn)了某些功能接口的對象底燎,完成某次調(diào)用可能對應(yīng)的對象就會被釋放了刃榨,如果調(diào)用頻率高會緩存到BHContext中弹砚。而BHModulesManager管理的對象它可能是存在比較長的時間,比如和App的聲明周期一樣枢希,像網(wǎng)絡(luò)庫桌吃,廣告SDK等,它和BHServiceManager管理的對象最大區(qū)別就是生命周期不同苞轿,它可能會參與到App或者自定義的事件中读存。對于實(shí)現(xiàn)組件化,常駐內(nèi)存的對象可以考慮封裝成BHModule對象呕屎,通過BHModuleManager來進(jìn)行管理,而臨時使用的接口敬察,比如一些數(shù)據(jù)處理對象秀睛,日期格式化對象(主要是那些只跟某些特定業(yè)務(wù)相關(guān)的對象,可能退出頁面就用不到了)可以通過Service的方式來進(jìn)行管理。
BHConfig/BHContext/BHWatchDog
BHConfig :對象是配置對象莲祸,提供了按照類型進(jìn)行存取的功能蹂安。
BHContext: 它存儲了Config對象,開發(fā)環(huán)境信息锐帜,以及自定義事件以及參數(shù)田盈,service緩存的對象也存放在這個對象之中。
BHWatchDog: 它是用來監(jiān)測主線程是否卡頓的對象缴阎,發(fā)生卡頓會打印日志提示允瞧。實(shí)現(xiàn)卡頓監(jiān)測的原理是封裝了一個子線程,在子線程中有一個標(biāo)記位蛮拔。通過子線程不斷的循環(huán)在循環(huán)中派發(fā)一個handler到主線程中述暂,派發(fā)到主線程中主要做的事情就是處理標(biāo)記位,之后通過設(shè)置的interval讓子線程睡眠對應(yīng)時間建炫,睡眠interval秒之后再通過標(biāo)記位來判斷主線程是否發(fā)生了卡頓畦韭。如果發(fā)生卡頓,主線程在interval時間內(nèi)可能來不及處理標(biāo)記位肛跌,然后由此推斷出發(fā)生了卡頓艺配。為了防止不必要的額外n次派發(fā),在子線程中增加了信號量來控制派發(fā)次數(shù)衍慎,在handler中會增加信號量转唉,每次循環(huán)結(jié)束會通過dispatch_semaphore_wait判斷信號量是否大于0,否則一直處于等待稳捆。
BHRouter路由實(shí)現(xiàn)
BHRouter: 通過URL的方式實(shí)現(xiàn)頁面跳轉(zhuǎn)酝掩,module的注冊,service注冊眷柔,以及對象方法的調(diào)用期虾。url的格式定義了解析的邏輯原朝,所以格式是固定的:
//url - > com.alibaba.beehive://call.service.beehive/pathComponentKey.protocolName.selector/...?params={}(value url encode)
//url - > com.alibaba.beehive://register.beehive/pathComponentKey.protocolName/...?params={}(value url encode)
//url - > com.alibaba.beehive://jump.vc.beehive/pathComponentKey.protocolName.push(modal)/...?params={}(value url encode)#push
在BHRouter中定義了BHRPathComponent對象,它可以理解成封裝了某個pathComponentKey的處理邏輯镶苞,如果需要使用BHRPathComponent喳坠,需要先注冊,pathComponentKey和Component的關(guān)系會存放到一個字典中茂蚓,后續(xù)在處理url的時候會通過pathComponentKey獲取對應(yīng)的Component進(jìn)行處理壕鹉。如果對url的處理邏輯相對簡單,沒有特別的需求聋涨,可以不使用BHRPathComponent晾浴。我能想到的復(fù)雜點(diǎn)的處理,比如對于頁面跳轉(zhuǎn)可能有一些特殊的需求牍白,不僅僅只是首頁跳轉(zhuǎn)到某個頁脊凰,可能是需要支持特定的頁面路徑,這種需求做成統(tǒng)一處理的邏輯是比較困難的茂腥,但是如果通過BHRPathComponent封裝起來狸涌,url解析那塊的邏輯會很清晰,不使用BHRPathComponent封裝會導(dǎo)致if else 或者 switch case邏輯很多不好維護(hù)最岗,所以這部分算是比較好的設(shè)計(jì)帕胆,最常使用的場景和一些復(fù)雜的場景都考慮到了,代碼的邏輯也不會很復(fù)雜般渡。在調(diào)用openUrl的時候懒豹,可以傳入?yún)?shù),這部分參數(shù)會和url里的query參數(shù)合并到一起驯用。
在push一個頁面的時候歼捐,除了需要動態(tài)創(chuàng)建一個vc的對象,還需要把一些參數(shù)傳給vc晨汹,這部分是通過kvc對vc的屬性進(jìn)行賦值豹储,再這之前會對參數(shù)進(jìn)行過濾,會判斷是否有對應(yīng)的property淘这,不存在的會從參數(shù)中刪除剥扣。
BHRouter本身提供了一種更加解耦的調(diào)用對象方法以及注冊service和model的方式,因?yàn)槭羌冏址畊rl的方式铝穷。成本在于拼接url需要按照特定規(guī)則钠怯,參數(shù)需要序列化成字符串json參數(shù)(這部分很難手寫出來)。如果需要處理頁面跳轉(zhuǎn)曙聂,是只能通過BHRouter來處理的晦炊。
BeeHive和BHAppDelegate
BeeHive封裝了BHServiceManager和BHModuleManager,提供了更加方便調(diào)用的注冊module以及注冊service創(chuàng)建service的接口。BHAppDelegate取代原始的Appdelegate断国,主要是為了更方便的處理App聲明周期相關(guān)的各個事件贤姆,把事件發(fā)送給所有module
reference: