[JSPatch]?初嘗JSPatch快速集成


JSPatch作為熱更新技術(shù)的黑科技,已經(jīng)不是什么前沿的新聞了,像騰訊、美團(tuán)等大公司也在使用JSPatch。前段時(shí)間蘋果對使用這些像JSPatch,weex等熱更新技術(shù)下發(fā)警告通知或強(qiáng)制下架的事,技術(shù)圈里讓很多小伙伴們坐不住了,鬧的沸沸揚(yáng)揚(yáng),這個(gè)15年就問世的框架具備很多之前的類似框架所不具備的優(yōu)點(diǎn),更加的小巧便捷,并且處于持續(xù)維護(hù)中,不僅如此,還由此成為了一個(gè)生態(tài)圈,bang神還為此開發(fā)了oc轉(zhuǎn)js的代碼轉(zhuǎn)換器、可以自動(dòng)提示的JSPatchX插件、以及基于這個(gè)技術(shù)的JSPatchPlatform平臺(tái)。總之讓大伙可以很方便的進(jìn)行patch。作為技術(shù)方面的一個(gè)小探索,抱著學(xué)習(xí)的態(tài)度,初次測試一下效果。

如何混淆JSPatch熱修復(fù)框架以繞過蘋果的機(jī)器檢測

  • HotFix概述
  • 集成JSPatch

<h3>HotFix概述</h3>

iOS中的HotFix方案總結(jié)詳解

對于iOS,這種HotFix方案大致可以分為四種:

  • WaxPatch(Alibaba)
  • Dynamic Framework(Apple)
  • React Native(Facebook)
  • JSPatch(Tencent)

WaxPatch
WaxPatch是一個(gè)通過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,同時(shí)性能上比較的優(yōu)秀,但是缺點(diǎn)也是非常的明顯,不符合Apple3.2.2的審核規(guī)則即不可動(dòng)態(tài)下發(fā)可執(zhí)行代碼,但通過蘋果JavaScriptCore.framework或WebKit執(zhí)行的代碼除外;同時(shí)Wax已經(jīng)長期沒有人維護(hù)了,導(dǎo)致很多OC方法不能用Lua實(shí)現(xiàn),比如Wax不支持block;最后就是必須要內(nèi)嵌一個(gè)Lua腳本的執(zhí)行引擎才能運(yùn)行Lua腳本;Wax并不支持arm64框架。


Dynamic Framework
動(dòng)態(tài)的Framework,其實(shí)就是動(dòng)態(tài)庫;首先我介紹一下關(guān)于動(dòng)態(tài)庫和靜態(tài)庫的一些特性以及區(qū)別。

不管是靜態(tài)庫還是動(dòng)態(tài)庫,本質(zhì)上都是一種可執(zhí)行的二進(jìn)制格式,可以被載入內(nèi)存中執(zhí)行。
iOS上的靜態(tài)庫可以分為.a文件和.framework,動(dòng)態(tài)庫可以分為.dylib(xcode7以后變成了.tdb)和.framework。

  • 靜態(tài)庫: 鏈接時(shí)完整地拷貝至可執(zhí)行文件中,被多次使用就有多份冗余拷貝。
  • 動(dòng)態(tài)庫: 鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,供程序調(diào)用,系統(tǒng)只加載一次,多個(gè)程序共用,節(jié)省內(nèi)存。

靜態(tài)庫和動(dòng)態(tài)庫是相對編譯期和運(yùn)行期的:靜態(tài)庫在程序編譯時(shí)會(huì)被鏈接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要改靜態(tài)庫;而動(dòng)態(tài)庫在程序編譯時(shí)并不會(huì)被鏈接到目標(biāo)代碼中,只是在程序運(yùn)行時(shí)才被載入,因?yàn)樵诔绦蜻\(yùn)行期間還需要?jiǎng)討B(tài)庫的存在。

  • 總結(jié):同一個(gè)靜態(tài)庫在不同程序中使用時(shí),每一個(gè)程序中都得導(dǎo)入一次,打包時(shí)也被打包進(jìn)去,形成一個(gè)程序。而動(dòng)態(tài)庫在不同程序中,打包時(shí)并沒有被打包進(jìn)去,只在程序運(yùn)行使用時(shí),才鏈接載入(如系統(tǒng)的框架如UIKit、Foundation等),所以程序體積會(huì)小很多。

好,所以Dynamic Framework其實(shí)就是我們可以通過更新App所依賴的Framework方式,來實(shí)現(xiàn)對于Bug的HotFix,但是這個(gè)方案的缺點(diǎn)也是顯而易見的它不符合Apple3.2.2的審核規(guī)則,使用了這種方式是上不了Apple Store的,它只能適用于一些越獄市場或者公司內(nèi)部的一些項(xiàng)目使用,同時(shí)這種方案其實(shí)并不適用于BugFix,更適合App線上的大更新。所以其實(shí)我們項(xiàng)目中的引入的那些第三方的Framework都是靜態(tài)庫,我們可以通過file這個(gè)命令來查看我們的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是只需要在項(xiàng)目中引入極小的JSPatch引擎,就可以使用JavaScript語言調(diào)用Objective-C的原生接口,獲得腳本語言的能力:動(dòng)態(tài)更新iOS APP,替換項(xiàng)目原生代碼、快速修復(fù)bug。但是JSPatch也有它的自己的缺點(diǎn),主要在由于它要依賴javascriptcore,framework,而這個(gè)framework是在iOS7.0以后才引入進(jìn)來,所以JSPatch是不支持iOS6.0的,同時(shí)由于使用的是JS的腳本技術(shù),所以在內(nèi)存以及性能上面是要低于Wax的。


<h3>集成JSPatch</h3>

JSPatch 需要使用者有一個(gè)后臺(tái)可以下發(fā)和管理腳本,并且需要處理傳輸安全等部署工作,JSPatch 平臺(tái)幫你做了這些事,提供了腳本后臺(tái)托管,版本管理,保證傳輸安全等功能,讓你無需搭建一個(gè)后臺(tái),無需關(guān)心部署操作,只需引入一個(gè) SDK 即可立即使用 JSPatch。

Github 開源的是 JSPatch 核心代碼,使用完全免費(fèi)自由,若打算自己搭建后臺(tái)下發(fā) JSPatch 腳本,可以直接使用 github 上的核心代碼,與 JSPatch 平臺(tái)上的 SDK 無關(guān)。JSPatch 平臺(tái)的 SDK 在核心代碼的基礎(chǔ)上增加了向平臺(tái)請求腳本/傳輸解密/版本管理等功能,只用于這個(gè)平臺(tái)。

官方接入文檔
注冊JSPatch平臺(tái)賬號

1.從官網(wǎng)上下載提供的SDK API包來后,導(dǎo)入工程,在TARGETS -> Build Phases -> Link Binary With Libraries -> + 添加 libz.dylib 和 JavaScriptCore.framework
2.生成和配置RSA密鑰
自定義 RSA 密鑰對 RSA 密鑰的作用詳見安全問題。目前為了更高的安全性,平臺(tái)強(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

生成的公私鑰,在上傳布丁時(shí)要用:


RSA 公私鑰

用JSPatch?官網(wǎng)工具中提供的RSA配置工具,拖入公鑰文件直接生成配置代碼


RSA生成配置代碼

注冊賬號成功后,在我的App中添加新應(yīng)用,應(yīng)用的圖標(biāo)生成是填寫了已上架應(yīng)用的Appkey,這里只是測試,就沒必要了,確定之后會(huì)生成平臺(tái)應(yīng)用的AppKey

添加新APP

3.在 AppDelegate.m中按順序調(diào)用startWithAppKeysetupRSAPublicKeysync方法,可以把 [JSPatch sync] 放在 -applicationDidBecomeActive: 里,每次喚醒都能同步更新 JSPatch 補(bǔ)丁,不需要等用戶下次啟動(dòng)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    /**
     *AppKey:JSPatch添加應(yīng)用時(shí)生成的AppKey
     *RSAPublicKey:剛才生成的公鑰RSA字符串
     */
    [JSPatch startWithAppKey:@"834abc498b14c64b"];
    [JSPatch setupRSAPublicKey:@"-----BEGIN PUBLIC KEY-----RSABLABLABLA45/44DJFJJNKSDLKS-----END PUBLIC KEY-----"];

    //用來檢測回調(diào)的狀態(tài),是更新或者是執(zhí)行腳本之類的,相關(guān)信息,會(huì)打印在你的控制臺(tái)
    [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)建一個(gè)laber,聲明一個(gè)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 **公鑰文件,因?yàn)橹皇菧y試,所以勾選開發(fā)預(yù)覽選項(xiàng),

  • 開發(fā)預(yù)覽:是用來測試開發(fā)用的
  • ?全量下發(fā):給所有安裝布丁的人下發(fā)
  • ?條件下發(fā):可以根據(jù)JSPatch設(shè)定的userId設(shè)定篩選條件下發(fā)
  • 灰度下發(fā):按人數(shù)灰度可以指定補(bǔ)丁對多少個(gè)用戶生效,超過設(shè)置的人數(shù)后不會(huì)再生效。灰度人數(shù)可以修改增加,但不能減少,可以逐漸增加灰度人數(shù),直到全量發(fā)布。
發(fā)布布丁

點(diǎn)擊提交之后,顯示發(fā)布成功


布丁詳情

7.再看看我們的demo,?打印臺(tái)收到如下消息就說明布丁更新加載成功

success

即使這樣,你還會(huì)發(fā)現(xiàn),laber的值并沒有改變啊,好吧,因?yàn)檠a(bǔ)丁是先下載再生效的,所以下一次運(yùn)行你才能看到效果,后續(xù)我會(huì)不斷去踩坑,這是我們在main.js 中設(shè)的值

屏幕快照 2017-04-22 21.48.26.png

可坑能踩的坑

  • 布丁腳本加載成功,卻出錯(cuò)MD5加密之類的,當(dāng)然就是你的?RSA公私鑰有問題嘍,重新生成一份
  • JSPatch網(wǎng)站上的版本要一定要和工程里的一樣
  • label的名字別寫錯(cuò)了
  • Swift一定要在方法和屬性前加dynamic,如果不是繼承自NSObject的Swift類不能被動(dòng)態(tài)替換
  • Swift替換類和方法要比OC在類/方法名之前添加工程名
  • 如果項(xiàng)目跑起來控制臺(tái)輸出沒有找到文檔就是網(wǎng)站上配置錯(cuò)了

相關(guān)連接:
JSPatch 基礎(chǔ)用法
JSPatch實(shí)現(xiàn)原理詳解:讓JS調(diào)用/替換任意OC方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,557評論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,255評論 4 61
  • Swift版本點(diǎn)擊這里歡迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh閱讀 25,573評論 7 249
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • 晚上在燈光是昏暗的,可這一點(diǎn)也不影響孩子去發(fā)現(xiàn)自然里的小生命。 在小區(qū)外圍的欄桿落的枯葉上,他發(fā)現(xiàn)了一只蝸牛。 他...
    平平無奇小個(gè)子閱讀 289評論 0 0