1. 前言
由本章節開始,我們將從支付寶客戶端的架構設計方案入手,細分拆解客戶端在“容器化框架設計”、“網路優化”、“效能啟動優化”、“自動化日誌收集”、“RPC 元件設計”、“移動應用監控、診斷、定位”等具體實現,帶領大家進一步瞭解支付寶在客戶端架構上的迭代與優化歷程。
本節將介紹支付寶 iOS 容器化框架設計的基本思路。
2. 容器化實現概覽
在 mPaaS 開篇介紹中已經和大家分享過《模組化與解耦式開發在螞蟻金服 mPaaS 中的實踐》:通過容器化開發框架將業務隔離成相對獨立的模組,並著力追求模組與模組之間高內聚、低耦合,因此我們實現了靈活的外掛式開發,並得以將業務劃分為上千個獨立工程。
mPaaS iOS 框架源自於支付寶客戶端,為了實現這種上千個工程之間的低耦合和相關依賴呼叫,mPaaS 框架直接接管了 App 的生命週期,負責整個 App 啟動託管、App 生命週期管理、處理與分發 UIApplication
的代理事件。
mPaaS 框架提供了容器化環境,業務開發人員在這個容器化環境中使用 微應用
和 服務
進行具體的業務需求開發。
微應用
和 服務
是 mPaaS 框架內定義的概念,主要是用來進行業務模組間的劃分。按照是否有 UI 介面作為標準,mPaaS 框架將不同的業務模組劃分為 微應用
和 服務
。微應用
是 APP 執行期間帶有使用者介面的業務模組;服務
是 App 執行期由業務提供的輕量級抽象服務。在 mPaaS 框架中,通過 框架上下文Context
進行 微應用
與 服務
的生命週期管理。
3. 應用生命週期管理
通過修改 main.m 函式的實現,mPaaS 框架使用自己的 ClientDelegate
類接管了 UIApplicationDelegate
中各種 App 生命週期。mPaaS 框架接入之後,ClientDelegate
完全替代了一般工程中的 AppDelegate
的角色,從而實現了整個應用的生命週期都是由框架進行管理。
int main(int argc, char * argv[]) {
@autoreleasepool {
// Now use mPaaS framework
return UIApplicationMain(argc, argv, @"Application", @"ClientDelegate");
}
}
複製程式碼
為了方便使用者獲取 App 生命週期來開發自定義功能,mPaaS 框架提供了 DTFrameworkInterface
類裡面實現了 UIApplicationDelegate
中所有代理方法的等價接入方式,只需要在 DTFrameworkInterface
的 Category 中覆蓋對應的方法即可。
例如下面常見的 UIApplicationDelegate
代理方法:
@protocol UIApplicationDelegate<NSObject>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (void)applicationDidBecomeActive:(UIApplication *)application;
- (void)applicationWillResignActive:(UIApplication *)application;
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation;
@end
複製程式碼
在 DTFrameworkInterface
中都提供了對應的方法:
typedef NS_ENUM(NSInteger, DTFrameworkCallbackResult)
{
DTFrameworkCallbackResultContinue = 0, // 繼續執行
DTFrameworkCallbackResultReturn = 1, // 中斷執行
DTFrameworkCallbackResultReturnYES = 2, // 中斷之後的邏輯,並返回 YES
DTFrameworkCallbackResultReturnNO = 3, // 中斷之後的邏輯,並返回 NO
};
@interface DTFrameworkInterface : NSObject
#pragma mark - 應用配置,微應用配置、服務配置、Scheme 處理器配置,可用 Category 覆蓋
- (void)application:(UIApplication *)application beforeDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (DTFrameworkCallbackResult)application:(UIApplication *)application handleDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (void)application:(UIApplication *)application afterDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (DTFrameworkCallbackResult)applicationDidBecomeActive:(UIApplication *)application;
- (DTFrameworkCallbackResult)applicationWillResignActive:(UIApplication *)application;
- (DTFrameworkCallbackResult)application:(UIApplication *)application
openURL:(NSURL *)url
newURL:(NSURL **)newURL
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation;
@end
複製程式碼
由於 mPaaS 框架有一些自己的初始化邏輯需要實現,在 DTFrameworkInterface
中額外提供了 beforeDidFinishLaunchingWithOptions
和 afterDidFinishLaunchingWithOptions
方法,方便使用者在 App 啟動時確定的時間執行自己的初始化程式碼。
DTFrameworkInterface
在 afterDidFinishLaunchingWithOptions
之前會啟動 BootLoader
,執行 mPaaS 框架的初始化邏輯。在嵌入式作業系統中,BootLoader
的作用是初始化硬體裝置,以便為最終呼叫作業系統核心準備好正確的環境。類似的在 mPaaS 框架中,BootLoader
用來初始化整個 mPaaS 框架環境,預設實現為依次執行下面的流程:
- 建立window
- 建立主NavigationController
- 執行那些只執行一次就可以,並且需要率先啟動的服務
- 啟動其它所有非lazyload的服務
- 啟動在ServicesMap的"[AUTOSTART]"陣列中指定需要自動啟動的服務分組
- 顯示主Window
- 啟動 Launcher 微應用,顯示出首頁
這樣就完成了 mPaaS 框架的初始化和首頁的顯示。
後面將詳細介紹其中關鍵的3個概念:微應用
、服務
、框架上下文 Context
。
4. 微應用
微應用就是帶 UI 介面的獨立業務模組,其中最特殊的一個微應用是 Launcher
微應用,Launcher
作為 App 啟動之後第一個開啟的微應用,一般用來建立 App 首頁。在 mPaaS 框架中,各個微應用之間是高度獨立、不相互依賴的。
微應用
通過 plist
檔案配置來進行註冊。配置微應用時需要指定 delegate
對應的類名、微應用的描述 description
以及開啟微應用時使用的 name
。這樣 框架上下文 Context
通過微應用的 name
就可以開啟指定的微應用。
為了方便業務開發,每個 微應用
也存在生命週期。微應用的生命週期,是模仿 iOS App 的生命週期來做的。每個微應用需要實現自己的 DTMicroApplicationDelegate
代理,這個類似於 iOS App 中實現的 Appdelege
類。
對於具體業務開發而言 微應用
的開發和一個完整的 App 一樣,每個 微應用
負責控制自己應用內的頁面堆疊,並根據 微應用
的生命週期執行相應的操作。在 mPaaS 框架中,所有的 微應用
都是執行在 mPaaS 框架提供的容器中,其不需要關注 App 的生命週期。對於一些特殊的業務場景,mPaaS 支援建立微應用的多個例項。
5. 服務
服務
與 微應用
不同地方在於其沒有 UI 介面,是在後臺執行。一旦服務啟動後,其在整個客戶端的生命週期中一直存在,因此服務一般用於給微應用提供通用服務,比如執行某個功能或者獲取資料等。
一個常見的服務是使用者登陸狀態服務,每個微應用可以通過這個服務來獲取到使用者的登入狀態和使用者資訊。
服務
也是通過 plist
檔案配置來進行註冊。服務註冊時需要提供服務的唯一標識 name
和對應的實現類 class
類名。框架在建立 服務
時會利用 Objective-C
語言的執行時機制建立 服務
實現類的例項。lazyLoading
用來控制是否延遲載入該類。如果是延遲載入,在框架啟動時該 服務
並不會例項化,只有在用到該 服務
時才會例項化並啟動。如果是非延遲載入,則在框架啟動時會啟動該服務。
由於服務的特殊性,在 mPaaS 中同時提供了 ServicesMap
來批量註冊類,ServicesMap
中的 [AUTOSTART]
用來說明哪些組的服務
需要在 App 啟動的時候最先啟動。
這種分級啟動服務的特點可以有效控制 App 的啟動時間,提供很好的使用者體驗。
每個服務都需要實現 服務
介面:
@required
/**
* 啟動一個服務。
* 注意:
* 框架在完成初始化操作後,會呼叫該方法。
* 如果一個服務要啟動一個應用,必須在該方法被呼叫之後,才能啟動其它的應用。
*/
- (void)start;
@optional
/**
* 建立服務完成。
*/
- (void)didCreate;
/**
* 服務將要銷燬。
*/
- (void)willDestroy;
複製程式碼
在增加了 服務
之後,整個 App 的結構如下圖所示。後臺的服務成為各個 微應用
之間溝通的橋樑。
6. 框架上下文 Context
通過前面的介紹,大家已經對 微應用
和 服務
有了深入的瞭解。在 mPaaS 框架中,框架上下文Context
承擔了一個排程員的角色,負責各個 微應用
和 服務
的排程、通訊管理,這樣就實現了每個 微應用
的開啟、頁面推棧以及關閉不會影響 App 其他 微應用
模組。
通過 mPaaS 框架提供的 DTContext * DTContextGet()
函式可以獲取到框架上下文Context
物件。一個簡化的 Context
類實現如下:
@interface Context : NSObject
@property(nonatomic, strong) UIWindow *window;
@property(nonatomic, strong) UINavigationController *navigationController;
// 根據指定的名稱啟動一個微應用
- (BOOL)startApplication:(NSString *)name params:(NSDictionary *)params animated:(BOOL)animated;
// 逆向遍歷微應用棧,返回最新的微應用物件。
- (DTMicroApplication *)findApplicationByName:(NSString *)name;
// 根據指定的名稱查到一個服務
- (id)findServiceByName:(NSString *)name;
// 註冊一個服務
- (BOOL)registerService:(id)service forName:(NSString *)name;
// 反註冊一個已存在的服務
- (void)unregisterServiceForName:(NSString *)name;
@end
複製程式碼
對於業務開發人員,可以通過 框架上下文 Context
獲取到主 window、啟動指定的 微應用
、獲取一個 服務
、動態註冊與反註冊 服務
,從而實現業務之間的連線。
7. 小結
通過本節內容,我們初步瞭解了 mPaaS 在 iOS 端容器化框架的設計思路,通過 微應用
和 服務
的方式完成業務模組之間的解耦和呼叫。框架上下文 Context
作為一個迷你的容器作業系統,為 微應用
和 服務
的執行提供了所需的容器化環境,保證了獨立的業務開發流程和流暢的使用者體驗。
歡迎大家體驗 mPaaS 容器化開發框架,期待大家的反饋與交流。
往期閱讀
《開篇 | 模組化與解耦式開發在螞蟻金服 mPaaS 深度實踐探討》