JSPatch作為熱更新技術(shù)的黑科技帆焕,已經(jīng)不是什么前沿的新聞了,像騰訊不恭、美團(tuán)等大公司也在使用JSPatch叶雹。前段時間蘋果對使用這些像JSPatch,weex等熱更新技術(shù)下發(fā)警告通知或強(qiáng)制下架的事换吧,技術(shù)圈里讓很多小伙伴們坐不住了折晦,鬧的沸沸揚(yáng)揚(yáng),這個15年就問世的框架具備很多之前的類似框架所不具備的優(yōu)點(diǎn)沾瓦,更加的小巧便捷满着,并且處于持續(xù)維護(hù)中,不僅如此贯莺,還由此成為了一個生態(tài)圈风喇,bang神還為此開發(fā)了oc轉(zhuǎn)js的代碼轉(zhuǎn)換器、可以自動提示的JSPatchX插件缕探、以及基于這個技術(shù)的JSPatchPlatform平臺魂莫。總之讓大伙可以很方便的進(jìn)行patch撕蔼。作為技術(shù)方面的一個小探索豁鲤,抱著學(xué)習(xí)的態(tài)度,初次測試一下效果鲸沮。
如何混淆JSPatch熱修復(fù)框架以繞過蘋果的機(jī)器檢測
- HotFix概述
- 集成JSPatch
<h3>HotFix概述</h3>
對于iOS,這種HotFix方案大致可以分為四種:
- WaxPatch(Alibaba)
- Dynamic Framework(Apple)
- React Native(Facebook)
- JSPatch(Tencent)
WaxPatch
WaxPatch是一個通過Lua語言編寫的iOS框架琳骡,不僅允許用戶使用 Lua 調(diào)用 iOS SDK和應(yīng)用程序內(nèi)部的 API, 而且使用了 OC runtime 特性調(diào)用替換應(yīng)用程序內(nèi)部由 OC 編寫的類方法,從而達(dá)到HotFix的目的讼溺。
WaxPatch的優(yōu)點(diǎn)在于它支持iOS6.0楣号,同時性能上比較的優(yōu)秀,但是缺點(diǎn)也是非常的明顯怒坯,不符合Apple3.2.2的審核規(guī)則即不可動態(tài)下發(fā)可執(zhí)行代碼炫狱,但通過蘋果JavaScriptCore.framework或WebKit執(zhí)行的代碼除外;同時Wax已經(jīng)長期沒有人維護(hù)了剔猿,導(dǎo)致很多OC方法不能用Lua實(shí)現(xiàn)视译,比如Wax不支持block;最后就是必須要內(nèi)嵌一個Lua腳本的執(zhí)行引擎才能運(yùn)行Lua腳本归敬;Wax并不支持arm64框架酷含。
Dynamic Framework
動態(tài)的Framework鄙早,其實(shí)就是動態(tài)庫;首先我介紹一下關(guān)于動態(tài)庫和靜態(tài)庫的一些特性以及區(qū)別椅亚。
不管是靜態(tài)庫還是動態(tài)庫限番,本質(zhì)上都是一種可執(zhí)行的二進(jìn)制格式,可以被載入內(nèi)存中執(zhí)行呀舔。
iOS上的靜態(tài)庫可以分為.a文件和.framework弥虐,動態(tài)庫可以分為.dylib(xcode7以后變成了.tdb)和.framework。
- 靜態(tài)庫: 鏈接時完整地拷貝至可執(zhí)行文件中媚赖,被多次使用就有多份冗余拷貝霜瘪。
- 動態(tài)庫: 鏈接時不復(fù)制,程序運(yùn)行時由系統(tǒng)動態(tài)加載到內(nèi)存省古,供程序調(diào)用粥庄,系統(tǒng)只加載一次,多個程序共用豺妓,節(jié)省內(nèi)存惜互。
靜態(tài)庫和動態(tài)庫是相對編譯期和運(yùn)行期的:靜態(tài)庫在程序編譯時會被鏈接到目標(biāo)代碼中,程序運(yùn)行時將不再需要改靜態(tài)庫琳拭;而動態(tài)庫在程序編譯時并不會被鏈接到目標(biāo)代碼中训堆,只是在程序運(yùn)行時才被載入,因為在程序運(yùn)行期間還需要動態(tài)庫的存在白嘁。
- 總結(jié):同一個靜態(tài)庫在不同程序中使用時坑鱼,每一個程序中都得導(dǎo)入一次,打包時也被打包進(jìn)去絮缅,形成一個程序鲁沥。而動態(tài)庫在不同程序中,打包時并沒有被打包進(jìn)去耕魄,只在程序運(yùn)行使用時画恰,才鏈接載入(如系統(tǒng)的框架如UIKit、Foundation等)吸奴,所以程序體積會小很多允扇。
好,所以Dynamic Framework其實(shí)就是我們可以通過更新App所依賴的Framework方式则奥,來實(shí)現(xiàn)對于Bug的HotFix考润,但是這個方案的缺點(diǎn)也是顯而易見的它不符合Apple3.2.2的審核規(guī)則,使用了這種方式是上不了Apple Store的读处,它只能適用于一些越獄市場或者公司內(nèi)部的一些項目使用糊治,同時這種方案其實(shí)并不適用于BugFix,更適合App線上的大更新罚舱。所以其實(shí)我們項目中的引入的那些第三方的Framework都是靜態(tài)庫井辜,我們可以通過file這個命令來查看我們的framework到底是屬于static還是dynamic揖赴。
React Native
React Native支持用JavaScript進(jìn)行開發(fā),所以可以通過更改JS文件實(shí)現(xiàn)App的HotFix抑胎,但是這種方案的明顯的缺點(diǎn)在于它只適合用于使用了React Native這種方案的應(yīng)用。
JSPatch
JSPatch是只需要在項目中引入極小的JSPatch引擎渐北,就可以使用JavaScript語言調(diào)用Objective-C的原生接口阿逃,獲得腳本語言的能力:動態(tài)更新iOS APP,替換項目原生代碼赃蛛、快速修復(fù)bug恃锉。但是JSPatch也有它的自己的缺點(diǎn),主要在由于它要依賴javascriptcore,framework,而這個framework是在iOS7.0以后才引入進(jìn)來呕臂,所以JSPatch是不支持iOS6.0的破托,同時由于使用的是JS的腳本技術(shù),所以在內(nèi)存以及性能上面是要低于Wax的歧蒋。
<h3>集成JSPatch</h3>
JSPatch 需要使用者有一個后臺可以下發(fā)和管理腳本土砂,并且需要處理傳輸安全等部署工作,JSPatch 平臺幫你做了這些事谜洽,提供了腳本后臺托管萝映,版本管理,保證傳輸安全等功能阐虚,讓你無需搭建一個后臺序臂,無需關(guān)心部署操作,只需引入一個 SDK 即可立即使用 JSPatch实束。
Github 開源的是 JSPatch 核心代碼奥秆,使用完全免費(fèi)自由,若打算自己搭建后臺下發(fā) JSPatch 腳本咸灿,可以直接使用 github 上的核心代碼构订,與 JSPatch 平臺上的 SDK 無關(guān)。JSPatch 平臺的 SDK 在核心代碼的基礎(chǔ)上增加了向平臺請求腳本/傳輸解密/版本管理等功能析显,只用于這個平臺鲫咽。
1.從官網(wǎng)上下載提供的SDK API包來后,導(dǎo)入工程谷异,在TARGETS -> Build Phases -> Link Binary With Libraries -> + 添加 libz.dylib 和 JavaScriptCore.framework
2.生成和配置RSA密鑰
自定義 RSA 密鑰對 RSA 密鑰的作用詳見安全問題分尸。目前為了更高的安全性,平臺強(qiáng)制要求所有補(bǔ)丁下發(fā)都使用自定義 RSA 密鑰歹嘹,生成 RSA 密鑰,在 Mac 終端上執(zhí)行 openssl箩绍,再執(zhí)行以下三句命令,生成 PKCS8 格式的 RSA 公私鑰尺上,執(zhí)行過程中提示輸入密碼材蛛,密碼為空(直接回車)就行圆到。
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
生成的公私鑰,在上傳布丁時要用:
用JSPatch?官網(wǎng)工具中提供的RSA配置工具卑吭,拖入公鑰文件直接生成配置代碼
注冊賬號成功后芽淡,在我的App中添加新應(yīng)用,應(yīng)用的圖標(biāo)生成是填寫了已上架應(yīng)用的Appkey豆赏,這里只是測試挣菲,就沒必要了,確定之后會生成平臺應(yīng)用的AppKey
3.在 AppDelegate.m中按順序調(diào)用
startWithAppKey
掷邦、setupRSAPublicKey
白胀、sync
方法,可以把 [JSPatch sync] 放在 -applicationDidBecomeActive: 里抚岗,每次喚醒都能同步更新 JSPatch 補(bǔ)丁或杠,不需要等用戶下次啟動
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/**
*AppKey:JSPatch添加應(yīng)用時生成的AppKey
*RSAPublicKey:剛才生成的公鑰RSA字符串
*/
[JSPatch startWithAppKey:@"834abc498b14c64b"];
[JSPatch setupRSAPublicKey:@"-----BEGIN PUBLIC KEY-----RSABLABLABLA45/44DJFJJNKSDLKS-----END PUBLIC KEY-----"];
//用來檢測回調(diào)的狀態(tài),是更新或者是執(zhí)行腳本之類的宣蔚,相關(guān)信息向抢,會打印在你的控制臺
[JSPatch setupCallback:^(JPCallbackType type, NSDictionary *data, NSError *error) {
NSLog(@"error-->%@",error);
switch (type) {
case JPCallbackTypeUpdate: {
NSLog(@"更新腳本 %@ %@", data, error);
break;
}
case JPCallbackTypeRunScript: {
NSLog(@"執(zhí)行腳本 %@ %@", data, error);
break;
}
case JPCallbackTypeCondition: {
NSLog(@"條件下發(fā) %@ %@", data, error);
break;
}
case JPCallbackTypeGray: {
NSLog(@"灰度下發(fā) %@ %@", data, error);
break;
}
default:
break;
} }];
[JSPatch setupDevelopment];
[JSPatch sync];
return YES;
}
4.在ViewController中創(chuàng)建一個laber,聲明一個test方法用來給laber賦值
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UILabel *textLaber;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.textLaber = [[UILabel alloc] initWithFrame:CGRectMake(0, 50, self.view.frame.size.width, 60)];
_textLaber.textAlignment = NSTextAlignmentCenter;
_textLaber.backgroundColor = [UIColor cyanColor];
[self.view addSubview:_textLaber];
[self test];
}
- (void)test{
self.textLaber.text = @"像瘋了一樣";
}
@end
5.創(chuàng)建main.js, 保存
console.log('run success')
defineClass("ViewController", {
test: function() {
self.textLaber().setText("內(nèi)容就這樣改變了");
},
})
6.就是發(fā)布布丁了胚委,布丁文件就是上面的main.js文件笋额,RSA密鑰就是生成的,**rsa_public_key.pem **公鑰文件篷扩,因為只是測試兄猩,所以勾選開發(fā)預(yù)覽選項,
- 開發(fā)預(yù)覽:是用來測試開發(fā)用的
- ?全量下發(fā):給所有安裝布丁的人下發(fā)
- ?條件下發(fā):可以根據(jù)JSPatch設(shè)定的userId設(shè)定篩選條件下發(fā)
- 灰度下發(fā):按人數(shù)灰度可以指定補(bǔ)丁對多少個用戶生效鉴未,超過設(shè)置的人數(shù)后不會再生效枢冤。灰度人數(shù)可以修改增加铜秆,但不能減少淹真,可以逐漸增加灰度人數(shù),直到全量發(fā)布连茧。
點(diǎn)擊提交之后核蘸,顯示發(fā)布成功
7.再看看我們的demo,?打印臺收到如下消息就說明布丁更新加載成功
即使這樣啸驯,你還會發(fā)現(xiàn)客扎,laber的值并沒有改變啊,好吧罚斗,因為補(bǔ)丁是先下載再生效的,所以下一次運(yùn)行你才能看到效果徙鱼,后續(xù)我會不斷去踩坑,這是我們在main.js 中設(shè)的值
可坑能踩的坑
- 布丁腳本加載成功,卻出錯MD5加密之類的袱吆,當(dāng)然就是你的?RSA公私鑰有問題嘍厌衙,重新生成一份
- JSPatch網(wǎng)站上的版本要一定要和工程里的一樣
- label的名字別寫錯了
- Swift一定要在方法和屬性前加dynamic,如果不是繼承自NSObject的Swift類不能被動態(tài)替換
- Swift替換類和方法要比OC在類/方法名之前添加工程名
- 如果項目跑起來控制臺輸出沒有找到文檔就是網(wǎng)站上配置錯了
相關(guān)連接:
JSPatch 基礎(chǔ)用法
JSPatch實(shí)現(xiàn)原理詳解:讓JS調(diào)用/替換任意OC方法