1. 程式人生 > >移動端基於動態路由的架構設計

移動端基於動態路由的架構設計

好久好久沒寫過文章了,一是最近專案太忙了,沒時間寫。二是也沒有時間學習新的東西,想寫點什麼卻又無從下筆。一味的去寫這個API怎麼用,那個新技術怎麼用,又顯的沒意思。沒有專案經驗總結的技術知識講解,總感覺有些蒼白。最近在做混合App開發這塊,從開始的ionic 框架,到後來的mui框架,讓我在混合開發這塊有了更深的理解,如果在這塊要寫點什麼無非漫天蓋地的這個指令怎麼用,那個模版怎麼用,資料怎麼進行雙向繫結,等等,但是這些網上已經很多資料了,等不太忙了,我想我會總結一篇這些框架的使用心得吧。但是我今天不講這個,我們來談一談在原生app中(iOS android)如何使用動態路由機制來搭建整個app的框架。
路由機制在web開發中是比較常見的,app開發中還是很少聽到這種概念的,目前有些大公司採用的元件化開發(手淘,攜程,蘑菇街等),倒是跟我們講的有很多相同之處,不過它們的比較複雜,而且網上很多元件化開發,路由機制,沒有一個能給出完整程式碼示例的,看著讓人云裡霧裡的。索性自己就借鑑它們的思想,加上一點個人的理解,搞出了一個簡單實用的可行性demo出來。我們主要介紹以下三方面內容:

1 什麼是動態路由
2 它能解決我們什麼問題
3 如何在專案中實現
一 什麼是動態路由

原生開發沒這概念,我們藉助angular路由機制來解釋這一概念,所謂路由,就是一套路徑跳轉機制,事先通過配置檔案定義好一個路徑對映檔案,跳轉時根據key去找到具體頁面,當然angular會做一些快取,頁面棧的管理等等一些操作,它大致的定義是這樣的

1234567891011121314151617181920212223 angular.module('app',[]).config('$routeProvider',function($routeProvider){$routeProvider.when('/',{templateUrl:'view/home.html',controller:'homeCtrl'}).when('/',{templateUrl:'view/home.html',controller:'homeCtrl'}).when('/',{
templateUrl:'view/home.html',controller:'homeCtrl'}).ontherwise({redirective:'/'})})

config函式是一個配置函式。在使用
$routeProvider這樣的一個服務。when:代表當你訪問這個“/”根目錄的時候 去訪問 templateUrl中的那個模板。 controller可想已知,就是我們配套的controller,就是應用於根目錄的這個 模板時的controller。
ontherwise 就是當你路徑訪問錯誤時,找不到。最後跳到這個預設的 頁面。
為此我們可以總結一下幾個特點:
1 一個對映配置檔案
2 路徑出錯處理機制
這就是路由的基本意思,我們看看,在原生開發中,採用此種方式,他能解決我們什麼問題。

二 它能解決我們什麼問題

首先我們來比較一下我們以前的結構模式以及與 加入路由機制後的專案結構,實現路由機制,不僅需要一個對映檔案,還需要一套路由管理機制,那麼採用路由機制,我們的專案架構就跟原來不一樣了,如下圖:

111972799-85e598d3bac2d57e 原先業務之間的呼叫關係.png 121972799-0488b6433e7d517a 加入路由後的頁面呼叫關係.png

接下來我們看一下平時我們採用的頁面跳轉方法:
iOS 下

Objective-C
12 [self presentViewController:controller animated:YES completion:nil];[self.navigationController pushViewController:controller animated:YES];

android 下

Java
1 Intent intent=newIntent(this,A.class);startActivity(intent);startActivityForResult(Intent intent,IntrequestCode)

我們看一下它有哪些缺點:
(1)都要在當前頁面引入要跳轉頁面的class 類。這就造成了頁面的耦合性很高。
(2)遇到重大bug,不能夠及時的修復問題,需要等待更新發版後才能解決。
(3)推送訊息,如果入口沒有關與頁面的引入處理,則不能跳轉到指定頁面。

引入路由機制後我們能否解決這些問題呢?
試想一下,如果我們通過一個配置檔案來對映頁面跳轉關係,而且通過反射機制來取消標頭檔案的引入問題,是不是我們就可以解決以上那些弊端了呢,比如,我們線上應用出現bug, 導致某個頁面一開啟,app就跪了,那我們是不是就可以通過更新路由配置檔案,把它對映到另一個頁面去:一個錯誤提示檔案,或者一個線上H5能實現相同功能的頁面。這樣的話,原生app也具有了一定的動態更新能力,其實想想還有很多好處,比如專案功能太多原生開發要很長時間,但是領導又急著要上線,那麼我們是不是就可以先開發一個網頁版的模組,app路由對映到這個web頁面,讓使用者先用著,等我們原生開發完了,然後再改一下對映檔案,沒升級的依舊用H5的路由,升級的就用原生的路由,如果H5頁面我們要廢棄了,那我們整體就可以路由到一個升級提升的頁面去了。

總結一下路由機制能解決我們哪些問題:
1 避免引入標頭檔案,是頁面之間的依賴大大變少了(通過反射動態生成頁面例項)。
2 線上出現重大bug,給我們提供了一個及時修補的入口
3 網頁和原生切換更方便,更自由。
4 可以跳轉任意頁面 例如我們常用的推送,要開啟指定的頁面,以前我們怎麼做的,各種啟動判斷,現在呢,我們只要給傳送訊息配個路由路徑就行了,開啟訊息,就能夠跳轉到我們指定的頁面。
等等,其它好處自行發掘。

三 如何在專案中實現

說了這麼多概念性問題,下面我們就用程式碼來實現我們的構想, 下面先以IOS平臺為例:
我們先看一下demo結構

131972799-243925c9e370669f iOS demo結構圖.png

說明:WXRouter 路由管理檔案
demo 路由使用示例
urlMap.plist 路由配置檔案
我們主要講解一下 WXRouter裡面的幾個檔案,以及ViewController檔案,還有urlmap.plist檔案,其他請下載示例demo,文末我會給出demo地址。

1234567 #import #import @interfaceWXRouter:NSObject+(id)sharedInstance;-(UIViewController *)getViewController:(NSString *)stringVCName;-(UIViewController *)getViewController:(NSString *)stringVCName withParam:(NSDictionary *)paramdic;@end
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 #import "WXRouter.h"#import "webView.h"#import "RouterError.h"#import "PlistReadUtil.h"#define SuppressPerformSelectorLeakWarning(Stuff) \do{_Pragma("clang diagnostic push")\_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")\Stuff;\_Pragma("clang diagnostic pop")\}while(0)@implementation WXRouter+(id)sharedInstance{staticdispatch_once_t onceToken;staticWXRouter *router;dispatch_once(&onceToken,^{router=[[WXRouter alloc]init];});returnrouter;}-(UIViewController *)controller:(UIViewController *)controller withParam:(NSDictionary *)paramdic andVcname:(NSString *)vcName{SEL selector=NSSelectorFromString(@"iniViewControllerParam:");if(![controller respondsToSelector:selector]){//如果沒定義初始化引數方法,直接返回,沒必要在往下做設定引數的方法NSLog(@"目標類:%@未定義:%@方法",controller,@"iniViewControllerParam:");returncontroller;}if(paramdic==nil){//如果引數為空 URLKEY 頁面唯一路徑標識別paramdic=[[NSMutableDictionary alloc]init];[paramdic setValue:vcName forKey:@"URLKEY"];SuppressPerformSelectorLeakWarning([controller performSelector:selector withObject:paramdic]);}else{[paramdic setValue:vcName forKey:@"URLKEY"];}SuppressPerformSelectorLeakWarning([controller performSelector:selector withObject:paramdic]);returncontroller;}-(UIViewController *)getViewController:(NSString *)stringVCName{NSString *viewControllerName=[PlistReadUtil plistValueForKey:stringVCName];Classclass=NSClassFromString(viewControllerName);UIViewController *controller=[[classalloc]init];if(controller==nil){//此處可以跳轉到一個錯誤提示頁面NSLog(@"未定義此類:%@",viewControllerName);returnnil;}returncontroller;}-(UIViewController *)getViewController:(NSString *)stringVCName withParam:(NSDictionary *)paramdic{UIViewController *controller=[selfgetViewController:stringVCName];if(controller!=nil){controller=[selfcontroller:controller withParam:paramdic andVcname:stringVCName];}else{//異常處理  可以跳轉指定的錯誤頁面controller=[[RouterError sharedInstance]getErrorController];}returncontroller;}@end

說明:通過反射機制根據傳入的string來獲取 viewcontroller例項,實現了兩個方法,一個是不需要傳入引數的,一個是需要傳入引數的,當跳轉到第二個頁面需要傳值 就使用第二個帶引數的方法,所傳的值通過NSDictionary來進行封裝,跳轉後的頁面通過實現
-(void)iniViewControllerParam:(NSDictionary *)dic 方法來獲取傳過來的引數。

123456 #import @interfacePlistReadUtil:NSObject@property(nonatomic,strong)NSMutableDictionary *plistdata;+(id)sharedInstanceWithFileName:(NSString *)plistfileName;+(NSString *)plistValueForKey:(NSString *)key;@end
1234567891011121314151617 #import "PlistReadUtil.h"@implementation PlistReadUtil+(id)sharedInstanceWithFileName:(NSString *)plistfileName{staticdispatch_once_t onceToken;staticPlistReadUtil *plistUtil;dispatch_once(&onceToken,^{plistUtil=[[PlistReadUtil alloc]init];NSString *plistPath=[[NSBundle mainBundle]pathForResource:plistfileName ofType:@"plist"];plistUtil.plistdata=[[NSMutableDictionary alloc]initWithContentsOfFile:plistPath];});returnplistUtil;}+(NSString *)plistValueForKey:(NSString *)key{PlistReadUtil *plist=[PlistReadUtil sharedInstanceWithFileName:@"urlMap"];return[plist.plistdata objectForKey:key];}@end

說明:路由配置檔案讀取工具類,我這裡讀取的是plist 檔案,我這裡也可以讀取json,或則訪問網路獲取後臺伺服器上的路由配置檔案,這個根據我們業務需求的不同,可以新增不同的讀取方法。

123456 #import #import @interfaceRouterError:NSObject+(id)sharedInstance;