动态更新,iOS有三种处理方案
1,开源框架reactive native,但是编程语言是js
iOS app可以在运行时从服务器拉去最新的js文件到本读,然后执行,因为js是一门动态的脚本语言
所以可以在运行时直接读取js文件执行,也因此能够实现iOS的热更新
2, lua脚本
Lua脚本如同js一样,也能在动态时被。之前愤怒的小鸟使用lua脚本做的一个插件wax,
可以实现使用lua写iOS应用。热更新时,从服务器拉去lua脚本然后
动态的执行就可以了。遗憾的是wax目前已经不更新了。
3, 使用oc语言的动态库framework。用oc进行热更新
http://blog.csdn.net/jianrenbubai/article/details/50351507
===================
步骤:
1,创建framework工程
iOS----framework&library----cocoa touch framework
2,代码处理
写一个controller的控制工具类
#import "HotUpdateControl.h" #import "AController.h" #import "BViewController.h" #import "CViewController.h" #import "DViewController.h" #import "EViewController.h" @implementation HotUpdateControl -(NSArray *)getVcs { return @[ [[AController alloc]init], [[BViewController alloc]init], [[CViewController alloc]init], [[DViewController alloc]init], [[EViewController alloc]init]]; } @end好了,开始打包framework,为了避免打包出来的framework,在真机上面运行不了,我们使用一个脚本来进行打包,目的是多型号CPU核心的合成,就是打出一个通用的包。
# Sets the target folders and the final framework product. # 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME # 例如: FMK_NAME = "MyFramework" FMK_NAME=${PROJECT_NAME} # Install dir will be the final output to the framework. # The following line create it in the root folder of the current project. INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework # Working dir will be deleted after the framework creation. WRK_DIR=build DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework # -configuration ${CONFIGURATION} # Clean and Building both architectures. xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build # Cleaning the oldest. if [ -d "${INSTALL_DIR}" ] then rm -rf "${INSTALL_DIR}" fi mkdir -p "${INSTALL_DIR}" cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/" # Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product. lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}" rm -r "${WRK_DIR}" open "${INSTALL_DIR}"
点击1,添加一个脚本2----HotUpdateScript,然后点击3,产生4的Run Script,然后把脚本复制进去,再点击5,选择脚本打包编译
复制到另一个项目的沙箱里面去,就可以给另外的项目使用
33,建立一个主项目,就是使用这些动态库的工程
现在进行读取离线包的测试,只要这个项目,能够从沙箱里面读取到代码文件,就意味着可以在线更新代码,远程升级,
我修改了UITabBarConroller加载板块的初始化方法,如果沙箱有framework动态库,就加载framework动态库上面的版块,令到项目可以模块化
// // TabController.m // HotUpdate // // Created by wukong on 15/12/18. // Copyright © 2015年 lhc. All rights reserved. // #import "TabController.h" //#import <HotUpdateMudel/HotUpdateControl.h> @interface TabController () @end @implementation TabController -(instancetype)initWithCoder:(NSCoder *)aDecoder{ if (self = [super initWithCoder:aDecoder]) { NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSArray* arrFramework = [self getFilenamelistOfType:@"framework" fromDirPath:documentDirectory]; NSLog(@"%@",arrFramework); if (arrFramework.count==0) { NSArray * arrTitle = @[@"首页",@"广场",@"朋友圈",@"我的",@"设置"]; NSMutableArray * arrVcs = @[].mutableCopy; for (int i=0; i<arrTitle.count; i++) { UIViewController * vcRoot = [[UIViewController alloc]init]; vcRoot.title = arrTitle[i]; vcRoot.view.backgroundColor = [UIColor whiteColor]; UINavigationController * navi = [[UINavigationController alloc]initWithRootViewController:vcRoot]; [arrVcs addObject:navi]; } [self setViewControllers:arrVcs animated:YES]; }else{ NSString *bundlePath = [NSString stringWithFormat:@"%@/%@",documentDirectory,[arrFramework lastObject]]; if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) { NSLog(@"file not exist ,now return"); return self; } NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; if (!bundle || ![bundle load]) { NSLog(@"bundle load error"); } Class loadClass = [bundle classNamed:@"HotUpdateControl"]; if (!loadClass) { NSLog(@"get bundle class fail"); return self; } NSObject *bundleObj = [loadClass new]; NSArray * arrVc = [bundleObj performSelector:@selector(getVcs)]; NSMutableArray * arrVcs = @[].mutableCopy; for (int i=0; i<arrVc.count; i++) { UIViewController * vcRoot =arrVc[i]; vcRoot.view.backgroundColor = [UIColor whiteColor]; UINavigationController * navi = [[UINavigationController alloc]initWithRootViewController:vcRoot]; [arrVcs addObject:navi]; } [self setViewControllers:arrVcs animated:YES]; } } return self; } -(NSArray *) getFilenamelistOfType:(NSString *)type fromDirPath:(NSString *)dirPath { NSArray *fileList = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:nil] pathsMatchingExtensions:[NSArray arrayWithObject:type]]; return fileList; } - (void)viewDidLoad { [super viewDidLoad]; } @end
NANANANANANANNANANANANANNANNANANANANANNANANANANANANANANANANANA
如果 是本地的默认版本,应该是
@[@"首页",@“广场”,@“朋友圈”,@“我的”,@“设置”]的模块
但如果是沙箱里面的模块,那么就应该是ABCDE
===================================================
Lua热更新
==========================================
Cocos2d-x已经封装了用于实现热更新功能的类,就是assetsmanager
api说明:
// 检测是否有版本更新 virtual bool checkUpdate();
// 下载更新的资源包并解压到下载路径 virtual void update();
// 获取当前客户端版本号 std::string getVersion();
// 删除客户端版本号 void deleteVersion();
// 设置下载回调(AssetsManagerDelegateProtocol) void setDelegate(AssetsManagerDelegateProtocol *delegate);
// 设置连接超时时间(单位:秒) void setConnectionTimeout(unsigned int timeout); // 设置从服务端下载资源包的url void setPackageUrl(const char* packageUrl);
// 设置服务端获取版本号的url void setVersionFileUrl(const char* versionFileUrl);
// 设置资源保存路径 void setStoragePath(const char* storagePath);
因为AssetsManager使用了pthread库,所以需要在win32工程中需要包含pthread库所在目录。
VS在工程属性——C/C++——常规——附加包含目录中添加:$(ProjectDir)..\..\..\cocos2dx\platform\third_party\win32\pthread
#include "UpdateLayer.h" #include "HelloWorldScene.h" #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) #include <dirent.h> #include <sys/stat.h> #endif bool UpdateLayer :: init (){ if ( CCLayer :: init ()) { // 设置资源包下载目录 m_downloadDir = CCFileUtils :: sharedFileUtils () -> getWritablePath (); m_downloadDir += "download" ; // 设置代理 getAssetsManager () -> setDelegate ( this ); // 添加资源包下载路径到搜索路径,优先搜索更新的资源 std :: vector < std :: string > searchPaths = CCFileUtils :: sharedFileUtils () -> getSearchPaths (); searchPaths . insert ( searchPaths . begin (), m_downloadDir ); CCFileUtils :: sharedFileUtils () -> setSearchPaths ( searchPaths ); // 提示 m_label = CCLabelTTF :: create ( "" , "Arial" , 18 ); m_label -> setAnchorPoint ( ccp ( 1 , 0.5 )); m_label -> setPosition ( ccp ( 465 , 20 )); addChild ( m_label ); // 菜单 CCMenu * menu = CCMenu :: create (); menu -> setPosition ( CCPointZero ); addChild ( menu ); CCSize visibleSize = CCDirector :: sharedDirector () -> getVisibleSize (); // 重置 CCMenuItemFont * itemReset = CCMenuItemFont :: create ( "reset" , this , menu_selector ( UpdateLayer :: reset )); itemReset -> setPosition ( ccp ( visibleSize . width / 2 , 50 )); menu -> addChild ( itemReset ); // 获取当前版本号 CCMenuItemFont * itemGetClientVersion = CCMenuItemFont :: create ( "getClientVersion" , this , menu_selector ( UpdateLayer :: getClientVersion )); itemGetClientVersion -> setPosition ( ccp ( visibleSize . width / 2 , 100 )); menu -> addChild ( itemGetClientVersion ); // 获取服务器最新版本 CCMenuItemFont * itemGetServerVersion = CCMenuItemFont :: create ( "checkUpdate" , this , menu_selector ( UpdateLayer :: checkUpdate )); itemGetServerVersion -> setPosition ( ccp ( visibleSize . width / 2 , 150 )); menu -> addChild ( itemGetServerVersion ); // 更新版本 CCMenuItemFont * itemUpdateVersion = CCMenuItemFont :: create ( "updateVersion" , this , menu_selector ( UpdateLayer :: update )); itemUpdateVersion -> setPosition ( ccp ( visibleSize . width / 2 , 200 )); menu -> addChild ( itemUpdateVersion ); // 进入场景 CCMenuItemFont * itemEnterScene = CCMenuItemFont :: create ( "enterScene" , this , menu_selector ( UpdateLayer :: enterScene )); itemEnterScene -> setPosition ( ccp ( visibleSize . width / 2 , 250 )); menu -> addChild ( itemEnterScene ); return true ; } return false ; } AssetsManager * UpdateLayer :: getAssetsManager (){ static AssetsManager * s_assetsManager = NULL ; if ( s_assetsManager == NULL ) { s_assetsManager = new AssetsManager ( "https://coding.net/u/linchaolong/p/Cocos2d-x_HotUpdate/git/raw/master/test.zip" , //下载资源包的url "https://coding.net/u/linchaolong/p/Cocos2d-x_HotUpdate/git/raw/master/version" , // 获取服务端版本号的url m_downloadDir . c_str ()); // 资源保存路径 s_assetsManager -> setDelegate ( this ); s_assetsManager -> setConnectionTimeout ( 3 ); } CCLOG ( "save path : %s" , s_assetsManager -> getStoragePath ()); return s_assetsManager ; } void UpdateLayer :: initDownloadDir (){ // 如果下载目录不存在,则创建下载目录 #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) DIR * pDir = NULL ; pDir = opendir ( m_downloadDir . c_str ()); if ( ! pDir ) { mkdir ( m_downloadDir . c_str (), S_IRWXU | S_IRWXG | S_IRWXO ); } #else if (( GetFileAttributesA ( m_downloadDir . c_str ())) == INVALID_FILE_ATTRIBUTES ) { CreateDirectoryA ( m_downloadDir . c_str (), 0 ); } #endif } void UpdateLayer :: deleteDir ( std :: string dir ){ // Remove downloaded files #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) std :: string command = "rm -r " ; // Path may include space. command += " \" " + dir + " \" " ; system ( command . c_str ()); #else std :: string command = "rd /s /q " ; // Path may include space. command += " \" " + dir + " \" " ; system ( command . c_str ()); #endif } void UpdateLayer :: onError ( cocos2d :: extension :: AssetsManager :: ErrorCode errorCode ){ switch ( errorCode ) { case cocos2d: : extension :: AssetsManager :: kCreateFile : CCLOG ( "error : create file failure" ); m_label -> setString ( "error : create file failure" ); break ; case cocos2d: : extension :: AssetsManager :: kNetwork : CCLOG ( "error : no network" ); m_label -> setString ( "error : no network" ); break ; case cocos2d: : extension :: AssetsManager :: kNoNewVersion : CCLOG ( "error : no new version" ); m_label -> setString ( "error : no new version" ); break ; case cocos2d: : extension :: AssetsManager :: kUncompress : CCLOG ( "error : uncompress file error" ); m_label -> setString ( "error : uncompress file error" ); break ; default: break ; } } void UpdateLayer :: onProgress ( int percent ){ char progress [ 80 ]; memset ( progress , '\0' , sizeof ( progress ) ); snprintf ( progress , sizeof ( progress ), "hotupdate downloading %d%%" , percent ); CCLOG ( "percent=%d %s" , percent , progress ); m_label -> setString ( progress ); } void UpdateLayer :: onSuccess (){ CCLOG ( "download success." ); m_label -> setString ( "download success." ); } void UpdateLayer :: update ( CCObject * pSender ){ // 初始化下载目录 initDownloadDir (); // 下载更新包 getAssetsManager () -> update (); } void UpdateLayer :: reset ( CCObject * pSender ){ if ( "" != m_downloadDir ) { // 删除下载目录 deleteDir ( m_downloadDir ); } // 删除版本号 getAssetsManager () -> deleteVersion (); } void UpdateLayer :: getClientVersion ( CCObject * pSender ){ CCString * msg = CCString :: createWithFormat ( "current client version : %s" , getAssetsManager () -> getVersion (). c_str ()); CCLOG ( "%s" , msg -> getCString ()); m_label -> setString ( msg -> getCString ()); } void UpdateLayer :: checkUpdate ( CCObject * pSender ){ if ( getAssetsManager () -> checkUpdate ()) { CCLOG ( "has new version" ); m_label -> setString ( "has new version" ); } else { CCLOG ( "has not new version" ); m_label -> setString ( "has not new version" ); } } void UpdateLayer :: enterScene ( CCObject * pSender ){ CCDirector :: sharedDirector () -> replaceScene ( HelloWorld :: scene ()); }