阿里開源組件化方案 - BeeHive
0. 概述
BeeHive 是用于 iOS 的 App 模塊化編程的框架實現方案,吸收了 Spring 框架 Service 的理念來實現模塊間的 API 耦合。
0.1 基本架構
0.2 實現特性
- 插件化的模塊開發運行框架
- 模塊具體實現與接口調用分離
- 模塊生命周期管理,擴展了應用的系統事件
0.3 設計原則
因為基于 Spring 的 Service 理念,雖然可以使模塊間的具體實現與接口解耦,但無法避免對接口類的依賴關系。
為什么不使用 invoke 以及動態鏈接庫技術實現對接口實現的解耦,類似 Apache 的 DSO 的方式?
主要是考慮學習成本難度以及動態調用實現無法在編譯檢查階段檢測接口參數變更等問題,動態技術需要更高的編程門檻要求。
0.4 項目名來源
BeeHive 靈感來源于蜂窩。蜂窩是世界上高度模塊化的工程結構,六邊形的設計能帶來無限擴張的可能。所以我們用了 BeeHive 來做為這個項目的命名。
1 模塊生命周期的事件
BeeHive 會給每個模塊提供生命周期事件,用于與 BeeHive 宿主環境進行必要信息交互,感知模塊生命周期的變化。
事件分為三種類型:
- 系統事件
- 通用事件
- 業務自定義事件
1.1 系統事件
系統事件通常是 Application 生命周期事件,例如 DidBecomeActive 、 WillEnterBackground 等。
系統事件基本工作流如下:
1.2 通用事件
在系統事件的基礎之上,擴展了應用的通用事件,例如 modSetup 、 modInit 等,可以用于編碼實現各插件模塊的設置與初始化。
擴展的通用事件如下:
1.3 業務自定義事件
如果覺得系統事件、通用事件不足以滿足需要,我們還將事件封裝簡化成 BHAppdelgate ,你可以通過繼承 BHAppdelegate 來擴展自己的事件。
2. 模塊注冊
模塊注冊的方式有靜態注冊與動態注冊兩種。
2.1 靜態注冊
通過在 BeeHive.plist 文件中注冊符合 BHModuleProtocol 協議模塊類:
2.2 動態注冊
@implementation HomeModule BH_EXPORT_MODULE() // 聲明該類為模塊入口 @end
在模塊入口類實現中 使用 BH_EXPORT_MODULE() 宏聲明該類為模塊入口實現類。
2.3 異步加載
如果設置模塊導出為 BH_EXPORT_MODULE(YES) ,則會在啟動之后第一屏內容展現之前異步執行模塊的初始化,可以優化啟動時時間消耗。
3. 編程開發
BHModuleProtocol 為各個模塊提供了每個模塊可以 Hook 的函數,用于實現插件邏輯以及代碼實現。
3.1 設置環境變量
通過 context.env 可以判斷我們的應用環境狀態來決定我們如何配置我們的應用。
-(void)modSetup:(BHContext *)context { switch (context.env) { case BHEnvironmentDev: //....初始化開發環境 break; case BHEnvironmentProd: //....初始化生產環境 default: break; } }
3.2 模塊初始化
如果模塊有需要啟動時初始化的邏輯,可以在 modInit 里編寫,例如模塊注冊一個外部模塊可以訪問的 Service 接口
-(void)modInit:(BHContext *)context { //注冊模塊的接口服務 [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]]; }
3.3 處理系統事件
系統的事件會被傳遞給每個模塊,讓每個模塊自己決定編寫業務處理邏輯,比如 3D-Touch 功能
-(void)modQuickAction:(BHContext *)context { [self process:context.shortcutItem handler:context.scompletionHandler]; }
3.4 模間調用
通過處理 Event 編寫各個業務模塊可以實現插件化編程,各業務模塊之間沒有任何依賴, core 與 module 之間通過 event 交互,實現了插件隔離。但有時候我們需要模塊間的相互調用某些功能來協同完成功能。
通常會有三種形式的接口訪問形式:
- 基于接口的實現 Service 訪問方式( Java spring 框架實現)
- 基于函數調用約定實現的 Export Method ( PHP 的 extension , ReactNatve 的擴展機制)
- 基于跨應用實現的 URL Route 模式( iPhone App 之間的互訪)
我們目前實現了第一種方式,后續會逐步實現后兩種方式。
基于接口 Service 訪問的優點是可以編譯時檢查發現接口的變更,從而及時修正接口問題。缺點是需要依賴接口定義的頭文件,通過模塊增加得越多,維護接口定義的也有一定工作量。
3.4.1 定義接口
以為 HomeServiceProtocol 為例。
@protocol HomeServiceProtocol <NSObject, BHServiceProtocol> - (void)registerViewController:(UIViewController *)vc title:(NSString *)title iconName:(NSString *)iconName; @end
3.4.2 注冊 Service
有三種方式:
聲明式注冊
@implementation HomeService BH_EXPORT_SERVICE() @end
API 注冊
[[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]];
BHService.plist 注冊
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>HomeServiceProtocol</key> <string>BHViewController</string> </dict> </plist>
3.4.3 調用 Service
#import "BHService.h" id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
3.5 單例與多例
對于有些場景下,我們訪問每個聲明 Service 的對象,希望對象能保留一些狀態,那我們需要聲明這個 Service 對象是一個單例對象。
我們只需要在 Service 對象中實現事件函數
聲明
-(BOOL) singleton { return YES; }
通過 createService 獲取的對象則為單例對象,如果實現上面函數返回的是 NO ,則 createService 返回的是多例。
id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
3.6 上下文環境Context
- 初始化設置應用的項目信息,并在各模塊間共享整個應用程序的信息
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [BHContext shareInstance].env = BHEnvironmentDev; //定義應用的運行開發環境 [BHContext shareInstance].application = application; [BHContext shareInstance].launchOptions = launchOptions; [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/CustomModulePlist";//可選,默認為BeeHive.bundle/BeeHive.plist [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/CustomServicePlist";//可選,默認為BeeHive.bundle/BHService.plist [[BeeHive shareInstance] setContext:[BHContext shareInstance]]; [super application:application didFinishLaunchingWithOptions:launchOptions]; id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)]; if ([homeVc isKindOfClass:[UIViewController class]]) { UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:(UIViewController*)homeVc]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.rootViewController = navCtrl; [self.window makeKeyAndVisible]; } return YES; }
更多細節可以參考Example用例。
4. 集成方式
cocoapods
pod "BeeHive", '1.0.0'