博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JSPatch的应用
阅读量:7097 次
发布时间:2019-06-28

本文共 6743 字,大约阅读时间需要 22 分钟。

hot3.png

在项目开发过程中, 为了实现热修复, 项目中集成了JSPatch框架.

为了更好的集成, 对JSPatch相关的操作完全封装到了一个类里面. 思路如下:

1. 首先调用一个类方法, 作为检测是否需要更新的入口.2. 从服务端请求数据, 请求服务端的补丁文件和对应的key(md5加密字符串, 目的: 安全传输/检测文件内容是否有变化).3. 请求结果存储到本地数据库. 比对请求到的key, 和本地存储的key对比.    1> 本地key和服务端请求到的key比对成功: 读取本地文件, 并执行js文件.    2> 比对失败(文件内容发生变化/本地不存在): 重新下载文件到本地, 并执行下载的js文件.    3> 本地存在的垃圾文件, 服务器返回结果没有的文件, 不做任何处理.    4> 统一清理本地的垃圾文件(以前下载产生的, 当前已经不需要的).

加密思路:

服务端:    1. 对每个文件进行md5加密, 这样, 如果文件发生了任何变化, key值就会发生变化, 移动端通过key值判断, 文件是否发生改变.    2. 对1的加密结果, 拼接一个约定的token, 再次加密. 因为, 这些js文件是非常敏感的, 万一下载途中被黑客截取, 黑客就能得到app中所有的方法和属性, 是非常不安全的. 加密后, 就算中途被黑客截取, 黑客也无法更改js文件, 从而, 移动端只执行从自己服务端下载的源文件.移动端:    1. 使用服务端完全相同的加密方法, 再次加密, 最后比对加密结果, 如果相同, 则执行对应js文件, 如果不同, 不做任何操作.

封装的 JSPatchHandler.h

////  JSPathHandler.h//  Elite////  Created by  www.6dao.cc on 16/10/8.//  Copyright © 2016年 ledao. All rights reserved.//#import 
@interface JSPatchHandler : NSObject/// 从服务器请求, 是否有新的补丁, 如果有新的, 则下载新的补丁, 然后打补丁+ (void)checkPatch;/// 不从服务器请求, 本地重新装载补丁文件+ (void)reloadingPatch;@end

JSPatchHandler.m

////  JSPathHandler.m//  Elite////  Created by  www.6dao.cc on 16/10/8.//  Copyright © 2016年 ledao. All rights reserved.//#import "JSPatchHandler.h"#import "JSPatchModel.h"#import "WHFileManager.h"#import "JPEngine.h"/** 处理逻辑:    1. 先调用一个类方法, 然后从服务器请求, 服务器中所有的补丁列表和对应的key.     2. 然后和本地存储的key对比:        1> 本地有 服务器端没有任何变化的文件, 不做任何处理.        2> 对比,本地文件和服务器文件发生了改变, 重新下载, 重新加载. 修改前和修改后, 相同的方法名, 会覆盖吗? 预测可以覆盖. 待检验.        3> 服务端新增文件, 重新下载, 重新加载. */static NSString *PatchDir = @"patch";const static NSString *secrityCode = @"0QEGR9123590u1";@interface JSPatchHandler ()@property (nonatomic, strong) NSOperationQueue *queue;@property (nonatomic, strong) NSArray *executedArray;@end@implementation JSPatchHandlerstatic NSArray *dataArray;+ (instancetype)sharePatchHandler {        static JSPatchHandler *patchHandler;        static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        patchHandler = [[JSPatchHandler alloc] init];        patchHandler.queue = [[NSOperationQueue alloc] init];                // 开启JPEngine        [JPEngine startEngine];    });        return patchHandler;}/// 从服务器请求, 是否有新的补丁/* 1. 从服务器获取补丁列表, 保存到本地数据库. 2. 检查列表中每一条信息, 如果本地存在, 则直接读取执行, 如果本地不存在或者有变化, 从网络去读取. 3. 网络数据, 下载后, 一方面, 保存到指定目录, 另一方面, 执行js文件, 4. 检查, 数据库中没有的文件名, 本地存在的文件名, 依次删除. */+ (void)checkPatch {        NSLog(@"___________________________________\n %@", NSHomeDirectory());        [AFNTool requestWithUrlString:@"app/file/listData" params:nil success:^(NSDictionary *response, BOOL success, NSString *code) {        if (!success) {            return ;        }                NSArray *patchArray = [AssignToObject customModel:@"JSPatchModel" fromArray:response[@"data"]];                // 请求结果 保存数据库中        [JSPatchModel delDataBaseTable];        [patchArray insertRecordFromArray];                // 检测并执行js代码        dispatch_async(dispatch_get_global_queue(0, 0), ^{            [[JSPatchHandler sharePatchHandler] checkAndExcute:patchArray];        });                // 删除本地存在的垃圾文件        dispatch_async(dispatch_get_global_queue(0, 0), ^{            [[JSPatchHandler sharePatchHandler] clearLocalData];        });    }];    }/// 不从服务器请求, 本地重新装载补丁文件+ (void)reloadingPatch {        dispatch_async(dispatch_get_global_queue(0, 0), ^{        NSArray *patchArray = [JSPatchModel getAllRecod];        [[JSPatchHandler sharePatchHandler] checkAndExcute:patchArray];    });}/// 检查本地是够存在, 如果存在去执行, 不存在去下载- (void)checkAndExcute:(NSArray *)array {            for (JSPatchModel *patchModel in array) {                if ([self verificationData:patchModel]) {            [self loadPatchFile:patchModel];        }else{            [self downloadPatchFile:patchModel];        }    }    }/// 下载js文件, 下载完成, 调用 loadingPatchFileWithModel: 加载js文件.- (void)downloadPatchFile:(JSPatchModel *)patchModel {        NSString *filePath = [[WHFileManager cacheDirWithSubpath:PatchDir] stringByAppendingPathComponent:patchModel.name];    [WHFileManager deleteFile:filePath];    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];    NSURL *url = [NSURL URLWithString:patchModel.downPath];    NSURLRequest *request = [NSURLRequest requestWithURL:url];    NSURLSessionDownloadTask *task =    [manager downloadTaskWithRequest:request                            progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {                                                                return [NSURL fileURLWithPath:filePath];                            }                   completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {                                              if (filePath) {                           [self loadPatchFile:patchModel];                       }                   }];    [task resume];    }/// 根据 data 数据包, 加载jsPatch/// 如果data度去过, 会被保存在 model 的data属性中, 如果没有读取, 则需要去读取- (void)loadPatchFile:(JSPatchModel *)patchModel {        if (![self verificationData:patchModel]) {        return ;    }        NSString *script = [[NSString alloc] initWithData:patchModel.data encoding:NSUTF8StringEncoding];    [JPEngine evaluateScript:script];}/// 根据 参数model, 判断参数中数据是否经过自己服务器加密, 如果是, 则可以执行jsdiamante, 如果不是, 不能执行- (BOOL)verificationData:(JSPatchModel *)patchModel {        NSString *filePath = [[WHFileManager cacheDirWithSubpath:PatchDir] stringByAppendingPathComponent:patchModel.name];    if (![WHFileManager fileExistsAtPath:filePath]) {        return NO;    }        if (!patchModel.data) {                patchModel.data = [WHFileManager readFile:filePath];    }        NSString *fileString = [[NSString alloc] initWithData:patchModel.data encoding:NSUTF8StringEncoding];    NSString *secrityKey = [[NSString stringWithFormat:@"%@%@", [fileString md5String], secrityCode] md5String];        return [secrityKey isEqualToString:patchModel.fileKey];}#pragma mark - 有空  给FMDBHelper, 加上一个队列操作数据库, 保证操作的同步执行 FMDBDatabaseQueue// 检查并删除, 本地存在但是数据库中没有记录的 记录- (void)clearLocalData {        NSString *filePath = [WHFileManager cacheDirWithSubpath:PatchDir];        // 获取数据库中记录    NSArray *records = [JSPatchModel getAllRecod];    NSArray *recordFileNames = [records valueForKeyPath:@"name"];    NSString *targetString = [recordFileNames componentsJoinedByString:@", "];        NSArray *localFiles = [WHFileManager contentsOfDirectoryAtPath:filePath];        for (NSString *fileName in localFiles) {        if (![targetString containsString:fileName] && ![fileName hasPrefix:@"."]) {            [WHFileManager deleteFile:[filePath stringByAppendingPathComponent:fileName]];        }    }}@end

WHFileManager 是自己对文件操作的相关封装, 文件操作还是比较简单的, 只是不经常用到, 经常忘掉 ?, 封装一下, 以后拿来就用.

JSPatchModel是一个数据Model类, 没什么好说的.

相关文件下载地址:

欢迎关注github.

如果您有更好的解决方案, 或者发现任何问题, 请联系:

转载于:https://my.oschina.net/whforever/blog/759628

你可能感兴趣的文章
python正则提取关键字
查看>>
php 中 set_time_limit 理解
查看>>
28 写函数,用户传入修改的文件名,与要修改的内容,执行函数,完成整个文件的批量修改操作(进阶)...
查看>>
MHA切换过程:
查看>>
HanLP汉语言分析框架
查看>>
SQLite 日期操作
查看>>
热词分享
查看>>
phpcms相关
查看>>
thinkphp空控制器的处理
查看>>
Unity优化----drawcall系列
查看>>
九章算法系列(#4 Dynamic Programming)-课堂笔记
查看>>
3月18日 全部练习题(二)
查看>>
Java synchronized详解
查看>>
Frameset使用教程
查看>>
局域网与internet
查看>>
request
查看>>
Beyond Compare乱码问题汇总
查看>>
线程和线程池
查看>>
Camstar开发常用数据库表及其关联
查看>>
html中的一些按钮之类的操作
查看>>