JSPath原理

    xiaoxiao2024-12-26  14

    一、用途

    是否有过这样的经历:新版本上线后发现有个严重的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

    转载请注明原文地址: https://ju.6miu.com/read-1294993.html
    最新回复(0)