JSPatch-动态更新IOS APP

来源:互联网 发布:淘宝服务器租用 编辑:程序博客网 时间:2024/05/02 04:38


一、用途

是否有过这样的经历:新版本上线后发现有个严重的bug,可能会导致crash率激增,可能会使网络请求无法发出,这时能做的只是赶紧修复bug然后提交等待漫长的AppStore审核,再盼望用户快点升级,付出巨大的人力和时间成本,才能完成此次bug的修复。

使用JSPatch可以解决这样的问题,只需在项目中引入JSPatch,就可以在发现bug时下发JS脚本补丁,替换原生方法,无需更新APP即时修复bug

二、原理

JSPatchiOS内置的JavaScriptCore.framework作为JS引擎,但没有用它JSExport的特性进行JS-OC函数互调,而是通过Objective-CRuntime,从JS传递要调用的类名函数名到Objective-C,再使用NSInvocation动态调用对应的OC方法。

三、通过实例了解JSPatch使用过程。

首先简单介绍一下这个实例要实现的功能

新建一个工程,有三个视图

视图1 ,rootViewController,

视图2,firstViewControoler,

视图3,sceondViewControll. (可以给firstViewControoler和SecondViewController分别加上不同的标题和背景色用以便好区分)

源代码中,视图1中有一个按钮,点击按钮进入视图2,代码如下

- (void)enterNextView:(id)sender

{

    FirstViewController* vc =[[FirstViewController alloc]init];

    [self.navigationControllerpushViewController:vc animated:YES];

}

现在我们要定义一个js文件,通过JSPatch框架来实现,点击按钮进入的视图不是视图2而是视图3.

1.首先需要引入JSPatch插件。

通过CocoaPod引入JSPatch插件:

pod ‘JSPatch’

工程引入系统框架 JavaScriptCore.framework

 

2.编写js代码(demo.js)。

require('SecondViewController')//声明引用的object c中的类

 

defineClass(rootViewController, {

            enterNextView: function(sender)  {

   var vc = SecondViewController.alloc().init()

   self.navigationController().pushViewController_animated(vc, YES)

 }

})

//defineClass覆盖rootViewController里原来的按钮点击事件enterNextView

3.加载js文件

 

-       (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

-       {

   // Override point for customization after application launch.

   

   [JPEngine startEngine];//启动JP引擎

  //加载本地demo.js文件。

   NSString *sourcePath = [[NSBundle mainBundle]pathForResource:@"demo" ofType:@"js"];

   NSString *script = [NSString stringWithContentsOfFile:sourcePathencoding:NSUTF8StringEncoding error:nil];

   [JPEngine evaluateScript:script];

   

   return YES;

}

运行程序 ,这时点击按钮,执行的不是rootViewController里的enterNextView函数,而是demo.js文件里的enterNextView,界面也就从视图1进入了视图3界面而不是视图2界面。

 

注:此处为了方便实现加载本地的js文件 ,真正应用的时候,需要把这个js文件放在服务器上程序启动时通过以下代码加载。

[JPEngine startEngine]; //启动JP引擎

 //下载服务器端js文件并加载到JPEngine

    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/bugfix.JS"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

    NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    if (script) {

      [JPEngine evaluateScript:script];

    }

四,JSPatch Loader使用

JSPatchLoader 负责根据版本号向服务端拉取 JSPatch代码,并对代码进行 RSA校验/解压/执行,整个校验原理在JSPatch部署安全策略 这篇文章里详细说明,不再复述。安全策略可参考http://blog.cnbang.net/tech/2879/.

1.安装

拷贝 Loader/ 目录下的文件到你的项目

2.配置

1)设 JPLoader.h的rootUrl 为你的服务器地址。脚本文件在服务器的存放路径是${rootUrl}/${appVersion}/${patchFile}

2)自行生成 RSA 公钥私钥,替换 JPLoader.h 里的 publicKey 和 tools/pack.php 里的 privateKey。

3脚本打包

JSPatch脚本文件规则:可以有多个 js 文件,脚本内可以调用 include() 接口包含,没有目录层级,必须包含一个 main.js 文件作为入口。

在命令行使用 Loader/tools/pack.php脚本打包 JS 文件,由用户放到自己的服务器上给客户端下载。

示例

$ php pack.php main.js other.js

会在当前目录生成 v1.zip 文件,打包了所有 js 文件并包含了校验文件。也可以在最后通过 -o 指定输出文件名:

$ php pack.php main.js -o v2

脚本文件名代表当前 patch 版本,与后续的+updateToVersion:callback: 接口相关。

4 加载

下载/更新脚本

客户端在得知服务端脚本有更新时,调用 +updateToVersion:callback:接口下载对应版本的脚本。至于如何得知服务端脚本更新可以自行定义,可以另外加个请求每次唤醒时询问服务器,也可以在 APP 原有的请求里加上这个信息。

举个例子,客户端当前 App 版本号为1.0,上述配置 rootUrl 变量配为 http://localhost/JSPatch/,服务端告诉客户端最新脚本版本号为2,于是调用 [JPLoader updateToVersion:2callback:nil],这时会去请求 http://localhost/JSPatch/1.0/v2.zip这个文件并解压验证,保存到本地目录等待执行。

执行脚本

通过 +run 接口执行已下载到本地的 JSPatch 脚本文件,建议在程序启动的 -application:didFinishLaunchingWithOptions: 里第一句调用这个接口,防止调用后执行 JSPatch 脚本过程中其他线程同时在执行相关代码,导致意想不到的问题。

5测试

在脚本文件还没打包上传到服务器前,可以先把文件加入项目工程 bundle 进行测试,加入后调用 +runTestScriptInBundle 就会执行项目工程里的 main.js 文件,并且 JS 脚本里 include() 接口也可以正常使用。

(参考https://github.com/bang590/JSPatch/wiki/JSPatch-Loader-使用文档

五、总结

要在项目中使用JSPatch实现动态更新ios,需要以下步聚

1.引入JSPatch和JavaScriptCore.framework

2.实现js文件并放到服务端供终端加载

(具本的实现js到object c转换的语法和需要注意事项目可参考文档https://github.com/bang590/JSPatch/wiki)

3.AppDelegate中加载服务端的js文件

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    [JPEngine startEngine];

    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/bugfix.JS"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

    NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    if (script) {

      [JPEngine evaluateScript:script];

    }

}];

   ….

    return YES;

}

@end

 

0 0
原创粉丝点击