二.Jspatch 简介:
JSPatch 是 iOS 上的动态更新框架,只需要引入小小的引擎文件,就可以用
JS 调用和替换任意 OC 方法,目前被普遍用于实时修复 bug。JSPatch 的基本原理就是通过 JS 代码,用 iOS 内置的 JavaScriptCore.framework 作为 JS 引擎, 通过Objective-C Runtime,从 JS 传递要调用的类名函数名到 Objective-C,再使用NSInvocation 动态调用对应的 OC 方法,以达到修改程序代码,让 App 具备 hit codepush 的能力。
JSPatch 需要使用者有一个后台可以下发和管理脚本,而且需要处理传输安全等部署工作,然后在客户端进行相应的下载执行脚本,以起到动态打补丁修复app 的功能。作者开发了 JSPatch 平台,提供了脚本后台托管,版本管理,保证传输安全等功能,用户只需在前端部署相应的sdk即可使用。但是,通过 JSPatch平台上传的脚本文件都会保存在七牛云存储上,且为收费服务,因此目前大多数的应用并没有采用 jspatch 平台的策略,而是自己搭建后台服务器,为前端提供相应的脚本文件加密、上传、灰度与条件下发、增量更新等功能。
三.Jspatch 部署(不使用 jspatch 平台方式)
1.后台服务器配置:(见文档 2.6、2.7、2.8、2.10)建议后台服务器中专门搭建一个远端仓库存放 js 文件,远端仓库的目录如
下图所示:
图1
其中,v1.zip 为上传至后台服务器的 js 打包压缩文件,可以有多个 js 文件,
脚本内可以调用 include() 接口包含,没有目录层级,必须包含一个 main.js 文件作为入口。
并且,后台必需提供脚本文件加密、上传功能,最好实现灰度与条件下发、增量更新、在线参数、实时监控、日志打印等功能(见文档 1.1)。
2.客户端 sdk 接入(见文档 2.1):
已经上线的 app 版本中必须已经接入 jspatch sdk 才能进行动态下载、启动 js
文件。其中接入 jspatch sdk 的方式有两种:1) cocoapods 方式:
platform:ios,'8.0'
target "TestLayerImage" dopod "JSPatch","~>1.1 "
end
2) 手动加入方式:
a.基本文件:
将 git 主页中 JSPatch/文件夹下的 JSEngine.m JSEngine.h JSPatch.js 文件
加入工程即可b.jspatch 扩展文件:
jspatch 除基本用法之外,还加入了其他的扩展接口,使用时可以直接将要的 git 主页中的 Extensions 文件夹或 Loader 文件夹直接放入工程中,需要注意的是,加载 Loader 文件时,工程中需加入 libz.1.tbd 动态库。
3.客户端下载、启动运行 js 文件
通过上述方式接入 sdk 的 app 版本,可以在运行中时,动态的从后台进行 js
文件的下载,下载后的 js 文件需进行启动配置后,才能在 app 中运行并发挥作
用。
1) 下载 js 文件:
可以通过 afnet working 等工具完成从后台下载 js 文件至客户端的功能,其中需要注意的是 js 文件的下载更新时机。当客户端在得知服务端脚本有更新时,才去下载对应版本的脚本。至于如何得知服务端脚本更新可以自行定义,可以另外加个请求每次唤醒时询问服务器,也可以在 APP 原有的请求里加上这个信息。
2) 运行 js 文件:
在 oc 中调用以下接口启动已下载到本地的 JSPatch 脚本文件。同样需要注意调用的时机问题。
a. 调用[JPEngine startEngine]
b. 通过[JPEngine evaluateScript:@"js代码串"]接口执行 JavaScript
3) 接口调用时机问题:(见文档 2.6)
我之前看到很多人把使用 js 和下载 js 的代码都放在了
didFinishLaunchingWithOptions:这个方法。我觉得有所不妥,因为如果这个 app用户一直放在手机的后台(比如微信),并且也没出现内存警告的话,这个方法应该一直不会调用。我建议的是:使用 js 文件的代码放在didFinishLaunchingWithOptions : 而 下 载 js 文 件 的 代 码 放 在applicationDidBecomeActive: 因为这个方法在程序启动和后台回到前台时都会调用。并且我建议设置一个间隔时间,根据一些_数据和权衡之后我们采用的是间隔时间设为 1 小时。 也就是说每次来到这个方法时,先要检测是距离上次发请求的时间间隔是否超过 1 小时,超过则发请求,否则跳过。
4. 使用扩展 JPLoader 简化后台管理与客户端下载、启动、配置工作:(见文档2.3、2.9)
JSPatch 使用时需要后台配合下发,中间有打包加密,解密解压校验,版本号管理等逻辑要处理,JSPatch Loader(JPLoader) 封装了这些逻辑,让使用者方便地搭建自己的后台下发流程。同时JPLoader还封装了在客户端下载后台js文件(加密后的压缩包文件)、对文件进行解压、RSA校验、执行的功能(校验原理见文档2.4中作者blog中的“JSPatch 部署安全策略”一文)。JSPatch Loader 做的事情包括:
1) 在本地打包和加密脚本文件
-
3) 根据版本号向服务端拉取上述打包后的文件
-
4) 对文件进行 RSA 校验/解压/执行
-
5) 执行本地已下载的脚本
注意:
-
1) 项目中加入JPLoader文件时,工程中需加入libz.1.tbd动态库。
-
2) 对于项目中的pch文件:在所在项目pch文件中,把自己所#import的头文
件做如下操作
#ifdef __OBJC__#import
#endif
3) 具体使用流程见文档2.3中的“JSPatch Loader 使用文档”一文、以及文档2.9
A. 生成 RSA 公钥私钥:
MAC OS自带了OpenSSL,直接在终端里使用openssl就可以
-
cd至要保存私钥/公钥的文件夹下
-
打开openssl
在终端中输入命令:openssl
-
在终端中输入命令: genrsa -out rsa_private_key.pem 1024
-
终端中输入命令: pkcs8 -topk8 -inform PEM -in rsa_private_key.pem-outform PEM –nocrypt
- 终端中输入命令: rsa -in rsa_private_key.pem -pubout -out
rsa_public_key.pem
-
此时会在文件夹下生成两个文件:
私钥文件:rsa_private_key.pem
公钥文件:rsa_public_key.pem
B. 配置 JPLoader 文件:
-
将 JPLoader.h 的 rootUrl 为你的服务器存放各 app 版本的 js 文件的
地址:
const static NSString *rootUrl = @"http://192.168.8.104/ShiZhuang";
-
将 JPLoader.h 中的 publicKey 替换为上步中生成的公钥文件中的公钥:
(终端中打开文件然后复制即可),注意换行使用换行符\n
C. 打包脚本文件:
-
Loader 文件夹下存在一个 pack.php 文件,将需要打包的 js 文件拷贝
至此目录下,并在终端中 cd 至此目录
-
终端中输入命令:php packer.php main.js other.js
会在当前目录生成 v1.zip 文件,打包了所有 js 文件并包含了校验文件。也可以在最后通过 -o 指定输出文件名:
$ php packer.php main.js -o v2
脚本文件名代表当前 patch 版本,与后续的+updateToVersion:callback: 接口相关。
D. 将上述打包完的脚本文件上传至服务器的对应文件夹下,如图 1 所示E. 配置客户端
-
脚本文件下载配置:
客户端在得知服务端脚本有更新时,调用 +updateToVersion:callback:接口下载对应版本的脚本。至于如何得知服务端脚本更新可以自行定义,可以另外加个请求每次唤醒时询问服务器,也可以在 APP 原有的请求里加上这个信息。
举个例子,客户端当前 App 版本号为 1.0,上述配置 rootUrl 变量配为 http://localhost/JSPatch/,服务端告诉客户端最新脚本版本号为2,于是调用 [JPLoader updateToVersion:2 callback:nil],这时会去请求http://localhost/JSPatch/1.0/v2.zip 这个文件并解压验证,保存到本地目录等待执行 -
脚本文件开启运行配置:
通过 +run 接口执行已下载到本地的 JSPatch 脚本文件,建议在程序启动的 -application:didFinishLaunchingWithOptions: 里第一句调用这个接口,防止调用后执行 JSPatch 脚本过程中其他线程同时在执行相关代码,导致意想不到的问题。
-