技术类文章收集大杂烩

一站式搞定 JSPatch 热修复

2017-01-05  本文已影响455人  Cean16

最近接触到热修复, 确实能解燃眉之急, 非常好用, 故分享给大家. 这里只讲 JSPatch, 这个是现在最热门最好用的框架, 用起来超级简单, 非常感谢 bang590 的贡献.

JSPatch 是一个开源项目, 只需要在项目里引入极小的引擎文件, 就可以使用 JavaScript 调用任何 Objective-C 的原生接口, 替换任意 Objective-C 原生方法. 目前主要用于下发 JS 脚本替换原生 Objective-C 代码, 实时修复线上 bug.

项目集成

[Github][1] 下载后, 按照[操作文档][2]操作就可以轻松集成, 摘录 bang590 Github 简要步骤如下:
[1]:https://github.com/bang590/JSPatch
[2]:https://github.com/bang590/JSPatch/blob/master/README-CN.md

// 方法一: 从网络拉回js脚本执行

[JPEngine startEngine];
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:kDownloadPath]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [JPEngine evaluateScript:script];
}];

// 上面代码 kDownloadPath 换成你自己的 JS 文件地址即可
// 每次都从网络拉取, 虽然文件小, 但也受限也网络状态, 不太理想.
// 方法二: 先下载到本地, 再从本地文件夹中读取

NSURLSession *session = [NSURLSession sharedSession]; 
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:kDownloadPath] completionHandler:^(NSURL * _Nullable location,NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"location: %@", location);

// 下载任务会把下载的资源存放到临时文件夹tmp下. block结束后, 就会自动删除.
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *path = [docPath stringByAppendingPathComponent:@"demo.js"];
NSLog(@"path: %@", path);// 拷贝路径在 Finder ->前往 ->前往文件夹 可看到已下载文件

// 测试了会有缓存, 且不能把原有的 JS 文件覆盖, 故要先移除
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
    [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
}

// 故把下载数据移动到document下
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:path]error:nil];
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    [JPEngine startEngine];
    [JPEngine evaluateScriptWithPath:path];
    }]; 
}];    
[task resume];

// 上面代码 kDownloadPath 换成你自己的 JS 文件地址即可

实际不需要每次都拉取, 该方法也只是暂缓措施, 下次迭代版本必须把上次 JS 修复的用原生解决, 这时需要有一个后台可以下发 JS 下载路径和管理脚本, 并且需要处理传输安全等部署工作.

JS 文件

JS 文件创建

JS 语法

defineClass(classDeclaration, [properties,] instanceMethods, classMethods)

@param classDeclaration: 字符串,类名/父类名和Protocol
@param properties: 新增property,字符串数组,可省略
@param instanceMethods: 要添加或覆盖的实例方法
@param classMethods: 要添加或覆盖的类方法

// 例如:
require('UIDevice');
defineClass("ViewController", {
    viewDidLoad: function() {
        var model = UIDevice.currentDevice().model();
        console.log(model);
        if (UIDevice.currentDevice().systemVersion().floatValue() >= 9) {
            console.log("9.0版本");
        } else {
            console.log("其他版本");
        }
        console.log("js 打印, 脚本号: 1.0, 替换实例成功");
    }
}, {
    test: function() {
        console.log("js 打印, 脚本号: 1.0, 替换类方法成功");
    }
});

- 在 JS 里面判断是否为空要判断 false

- ```java
    var url = "";
    var rawData = NSData.dataWithContentsOfURL(NSURL.URLWithString(url));
    if (rawData != null) {} //这样判断是错误的
    应该如下判断:
    if (!rawData){}
    在JSPatch.js源码里_formatOCToJS方法对undefined,null,isNil转换成了false。

版本管理

公司搭建后台

自己公司搭建后台, 除了下发拉取 JS 的地址外, 还可以加入一些参数, 比如: 版本控制, 指定修复某 iOS 版本等等, 条件根据需求定, 跟一般请求无异, 就不叙述了.

七牛云平台

JS 文件也可以存放到七牛云上, 七牛云同样提供版本控制, 这样自己公司后台省很多事, 只需写一个接口, 而且有一定的免费额度, 足够用了.

七牛云平台.png
七牛云使用流程
添加对象存储.png 创建储存空间.png 内存管理.png 上传文件.png 文件缓存时间.png

JSPatch 平台

不想搭建后台, 可以使用 JSPatch 平台, 也不用把 JS 文件上传到七牛云, 直接上传到 JSPatch 平台即可, 功能很多, 还提供条件下发, 平台文档介绍已经非常详细了, 这里就不再赘述了.

不过平台是需要收费的

JSPatch平台收费.png
!!!使用 JSPatch 平台注意点
RSA 文件.png

NSString *keyPath = [[NSBundle mainBundle] pathForResource:@"rsa_public_key" ofType:@"pem"];
NSString *publicKey = [NSString stringWithContentsOfFile:keyPath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"publicKey: %@", publicKey);
[JSPatch setupRSAPublicKey:publicKey];
//下方是 JSPatch 启动代码
[JSPatch startWithAppKey:@"19ed6339k440fa3ab"];

ifdef DEBUG

[JSPatch setupDevelopment];

endif

[JSPatch sync];

    

######集成错误录

- 若使用 XCode8 接入,需要在项目 Capabilities 打开 Keychain Sharing 开关,否则在模拟器下载脚本后会出现 `decompress error, md5 didn't match` 错误(真机无论是否打开都没问题)

- pod JSPatch 平台 SDK 完成并添加依赖库, 启动 `startWithAppKey` 和 `sync` 后报错

- ```objc
duplicate symbol _OBJC_METACLASS_$_JPEngine in:
    /Users/issuser/Library/Developer/Xcode/DerivedData/ViewController-ajurqnqqgeaehfajwnvxgpblrcmz/Build/Intermediates/ViewController.build/Debug-iphonesimulator/ViewController.build/Objects-normal/x86_64/JPEngine.o
    /Users/issuser/Library/Developer/Xcode/DerivedData/ViewController-ajurqnqqgeaehfajwnvxgpblrcmz/Build/Products/Debug-iphonesimulator/JSPatch/libJSPatch.a(JPEngine.o)
ld: 11 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

 //原因是工程里有手动导入 JSPatch.h JSPatch.m 和 JSPatch.js 文件, 和 cocoapods 冲突了

小结

JSPatch 热修复集成简单吧, 难点在 JS 语法上, 没有语法提示, 写的时候更要细心.
如果没有效果的话, 检查 JS 语法是否正确, 也可以通过 Safari 的调试工具对 JS 进行断点调试, 详见 JS 断点调试, 还有是否执行之前缓存的文件.

上一篇下一篇

猜你喜欢

热点阅读