ios app 實現熱更新(無需發新版本實現app添加新功能)
目前能夠實現熱更新的方法,總結起來有以下三種
1. 使用FaceBook 的開源架構 reactive native,使用js寫原生的ios應用
ios app可以在運行時從伺服器拉取最新的js檔案到本地,然後執行,因為js是一門動態
指令碼語言,所以可以在運行時直接讀取js檔案執行,也因此能夠實現ios的熱更新
2. 使用lua 指令碼。lua指令碼如同js 一樣,也能在動態時被。之前憤怒的小鳥使用
lua指令碼做的一個外掛程式 wax,可以實現使用lua寫ios應用。熱更新時,從伺服器拉去lua指令碼
然後動態執行就可以了。遺憾的是 wax目前已經不更新了。
上面是網上現在能夠搜到的熱更新方法。
xcode 6 之後,蘋果開放了 ios 的動態庫編譯許可權。所謂的動態庫,其實就是可以在運行時載入。
正好利用這一個特性,用來做ios的熱更新。
1.
建立一個動態庫,
動態庫包含需要使用的viewCOntroller,當然可以包含任何需要使用的自訂ui和邏輯。
動態庫的入口是一個jkDylib的類。它的.h和.m檔案分別如下:
//// JKDylib.h// JKDylb//// Created by wangdan on 15/7/5.// Copyright (c) 2015年 wangdan. All rights reserved.//#import @interface JKDylib : NSObject-(void)showViewAfterVC:(id)fromVc inBundle:(NSBundle*)bundle;@end
.m檔案
//// JKDylib.m// JKDylb//// Created by wangdan on 15/7/5.// Copyright (c) 2015年 wangdan. All rights reserved.//#import JKDylib.h#import JKViewController.h@implementation JKDylib-(void)showViewAfterVC:(id)fromVc inBundle:(NSBundle*)bundle{ if (fromVc == nil ) { return; } JKViewController *vc = [[JKViewController alloc] init]; UIViewController *preVc = (UIViewController *)fromVc; if (preVc.navigationController) { [preVc.navigationController pushViewController:vc animated:YES]; } else { UINavigationController *navi = [[UINavigationController alloc] init]; [navi pushViewController:vc animated:YES]; } }@end
上述代碼意圖非常明顯,
就是調用該動態庫的時候
-(void)showViewAfterVC:(id)fromVc inBundle:(NSBundle*)bundle
在該函數中,建立一個viewController 然後使用mainBundler 的navigationController push 建立的viewController,顯示動態庫的ui介面。
而動態庫中的JKViewController 內容則可以根據需要隨便定義。
2. 完成上述動態庫的編譯工作後,現在需要做的就是在主工程中,寫一段載入該動態庫的代碼。
主工程目錄如下:
在最重要的viewCotrooler裡面,定義了載入動態庫的方法:
//// ViewController.m// DylibTest//// Created by wangdan on 15/7/5.// Copyright (c) 2015年 wangdan. All rights reserved.//#import ViewController.h#import AFNetWorking.h@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; self.title = @bundle test; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init]; manager.responseSerializer = [AFJSONResponseSerializer serializer]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@http://www.baidu.com]]; [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@request success); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@request failure); }]; UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 100, 50)]; btn.backgroundColor = [UIColor blueColor]; [btn addTarget:self action:@selector(btnHandler) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn]; NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; BOOL writeResult = [@hellow writeToFile:[NSString stringWithFormat:@%@/%@,document,@hello.plist] atomically:YES encoding:NSUTF8StringEncoding error:nil]; // Do any additional setup after loading the view, typically from a nib.}-(void)btnHandler{ //AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init]; //manager.responseSerializer = [AFJSONResponseSerializer serializer]; // NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@http://www.baidu.com]]; // [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { // NSLog(@request success); // } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // NSLog(@request failure); //}]; NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *bundlePath = [NSString stringWithFormat:@%@/%@,documentDirectory,@JKDylb.framework]; if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) { NSLog(@file not exist ,now return); return; } NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; if (!bundle || ![bundle load]) { NSLog(@bundle load error); } Class loadClass = [bundle principalClass]; if (!loadClass) { NSLog(@get bundle class fail); return; } NSObject *bundleObj = [loadClass new]; [bundleObj performSelector:@selector(showViewAfterVC:inBundle:) withObject:self withObject:bundle]; NSString *framePath = [[NSBundle mainBundle] privateFrameworksPath]; NSLog(@framePath is %@,framePath); NSLog(@file attri %@,bundle.localizations); // [bundleObj showViewAfterVC:self inBundle:bundle];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}@end
viewController視圖中有一個按鈕,點擊按鈕後,從 document目錄下面找到動態庫(雖然此時document下並沒有動態庫),動態庫的名稱約定好味
JKDylib.framework
然後使用NSBundle 載入該動態庫,具體見代碼。
載入成功後,調用在動態庫中實現的方法
[bundleObj performSelector:@selector(showViewAfterVC:inBundle:) withObject:self withObject:bundle];
編譯該工程,然後運行到手機上,然後退出該程式
3. 開啟itunes 然後將動態庫同步到剛才的測試工程目錄下。
4.在此開啟測試工程程式,點擊button,則會發現能夠進入在動態庫中定義的ui介面了。
關於動態更新的思考:
採用動態庫方式實現熱更新其實還是有一個問題,就是如何在主工程和動態庫之間共用組建
比如網路組件以及其他等等第三方組件。
目前我沒發現好方法,只能在動態庫和主工程之間分別添加並且編譯。