淺析JSPatch的使用

淺析JSPatch的使用

一. 背景介紹

  • 背景:iOS作為蘋果獨家開發和運營的生態圈,具有非常封閉的運作環境,其App上線需要通過提交,排隊,審核,上線這四個很長的過程,過往的排隊時間長至于7~15天,對一個軟件迭代就有了極大的限制,如果產品中出現了小bug, 需要修復問題的話就是及其困難的了,雖然目前蘋果的審核時間已經短至1天,但是其App更新方式為全量更新,如若為了修復一個極小的問題而強迫用戶更新一個版本的話,體驗也是不好的, 所以在多年的技術進化中,產生了App熱更新這樣的一個需求。

  • 發展: 在這幾年來的iOS開發界,各個廠商和各種極客各現神通,通過各種黑科技來實現各種熱更新技術的實現,在iOS7之前,著名的有Wax框架,Wax框架主要使用Lua語言進行腳本實現,并且需要在原生的App中植入Wax的腳本引擎,使用上也不是太方便,所以逐漸被淘汰。2013年,蘋果在iOS7中引入了原生框架JavascriptCore這樣一個原生框架,徹底打開了JS腳本和native調用之間的橋梁,也為熱更新技術的實現提供了原生的技術支持,各種極客和軟件開發商都定制自己的腳本來調用本地的部分代碼,但是都沒有統一的方案,在這一個沉淀的過程中,產生了較為成熟易用的JSPatch框架。

二. JSPatch介紹

  • 誕生:JSPatch誕生于2015年5月,最初出自于騰訊廣研院的高級iOS開發工程師@bang的個人項目,其超級深厚的開發基礎和過人的天賦在這個項目中表現的淋漓盡致,其基本原理為使用javascriptcore中提供的調用Objective-C的原生入口,并充分利用Objective-C的動態特點來在運行時修改方法的入口和實現,用于替換原有的舊方法。 目前JSPatch在Github上已經開源,擁有大量的擁簇,并且充分在騰訊,阿里,百度各個大廠的產品中得到了驗證,騰訊甚至提供了JSPatch托管SDK平臺用于商用,可見其實現已經成熟到了商用的地步,可以放心使用了。以下為其他開發者總結的JSPatch和Wax的區別:
JSPatch_Wax.jpg

基本原理

  • Objective-C是動態語言,具備運行時特性,所以能夠在運行時通過類名稱或者方法的名稱來獲取執行入口,并且進行實際調用,而且還可以通過runtime特性來swizzle各種方法,進行動態修改。
  • 通過類和方法名稱進行運行時調用的片段:
Class class = NSClassFromString(@"UIViewController");        
id viewController = [[class alloc] init];
SEL selector = NSSelectorFromString(@"viewDidLoad");
[viewController performSelector:selector];

動態替換類方法為新方法的片段:

Class cls = objc_allocateClassPair(superCls, "JPObject", 0);
objc_registerClassPair(cls);
class_addMethod(cls, selector, implement, typedesc);

正是由于OC語言具有如上片段展示的runtime時決定調用方法的特性,成就了熱更新的基礎原理。

具體的實現原理比較復雜,作者已經開源了代碼,并且在博客中寫明了實現過程中的各種問題,如果有興趣的可以參閱以下鏈接:
JSPatch在github的源地址,
JSPatch詳細實現原理

三. 使用方法

  • 基本語法解析

下面展示一下OC代碼和熱更新的JS腳本的對應代碼片段,例如線上 APP 有一段代碼出現 bug 導致 crash:

@implementation JPTableViewController
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSString *content = self.dataSource[[indexPath row]];  //可能會超出數組范圍導致crash
  JPViewController *ctrl = [[JPViewController alloc] initWithContent:content];
  [self.navigationController pushViewController:ctrl];
}
...
@end

可以通過下發這樣一段 JS 代碼,覆蓋掉原方法,修復這個 bug:

//JS
defineClass("JPTableViewController", {
  //instance method definitions
  tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
    var row = indexPath.row()
    if (self.dataSource().length > row) {  //加上判斷越界的邏輯
      var content = self.dataArr()[row];
      var ctrl = JPViewController.alloc().initWithContent(content);
      self.navigationController().pushViewController(ctrl);
    }
  }
}, {})

除了修復一些bug之外,也可以動態的修改一些App的行為, 詳細越發可以查閱
JSPatch語法WIKI

  • SDK接入使用

前面已經提到,騰訊已經將JSPatch進行了商用包裝,使用方式非常簡單,進入JSPatch的官方站點,下載SDK,將SDK拖入工程,然后添加官方javascriptcore.framework就可以按照文檔進行使用了,騰訊官方還進行了后端平臺托管,使用者在后端注冊App獲取Key,然后在App端初始化即可使用,需要熱更新的腳本也配置在后端進行下發,使用非常方便簡潔,該平臺提供每天1W次下發的免費量,超過1W次需要收費服務,詳細使用方式可以查閱:
JSPatch官方網站以及介紹

  • 源代碼引入使用

首先構建一個簡單的demo, demo潔面只有一個按鈕,按鈕下面一個Label框,demo的簡單邏輯是,點擊按鈕之后,Label框背景顏色變為藍色,并且顯示native code字樣。

原始代碼片段如下:

demoSource.png

初始運行界面為:

demoOne.png

點擊changeColor按鈕之后,事件響應并執行,運行界面變為:

demoBlue.png

以上為native代碼的原始邏輯,下面我們開始進行動態替換:

  • 思路

我們要動態替換changeColor的點擊事件,其實就是需要動態替換clickChangeColorEvent這個函數,大家可以去按照上面的JSPatch的語法wiki中去查閱詳細語法,我們這里有更簡單直接的方法,JSPatch的作者為了使用者方便,開發了
JSPatchConverter這個工具,大家可以去詳細看一下使用,也可以直接下載成品App, 我們這里使用成品App進行JS代碼生成,只需要在App的左側寫入要替換的方法,點擊convert按鈕,右邊窗口即可生成熱更新的腳本代碼,如圖:

jsconverter.png

將右邊窗口的成品JS片段自己保留出來,然后放在自建的服務器準備下發使用。

  • 本地工程準備

從github中下載JSPatch的源碼,并在源碼工程中找到JSPatch文件夾,將該文件夾拖入demo工程,如圖:

treeOne.png

同時添加蘋果官方的javascriptcore.framework,如圖:

treeTwo.png
  • 代碼實現

首先要在AppDelegate.h中添加對應的頭文件:

#import "JSPatch/JPEngine.h"

繼而在iOS的App啟動入口函數中添加JSEngine的初始化:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [JPEngine startEngine];
    
    return YES;
}

以上兩步已經完成了JSEngine的簡單的初始化過程,然后進入下一步,獲取遠端腳本的過程。 將前面在JSConverter中生成的腳本代碼自己放入自建服務器中(這一步自己尋找后端兄弟協助完成), 然后在實現一個獲取腳本的函數,進行腳本獲取,并將執行獲取腳本的函數放在[JPEngine startEngine]之后執行,然后在獲取到腳本之后,執行腳本行為,以下為完整范例:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [JPEngine startEngine];
    [self getAndRunJSPatchScript];
    return YES;
}

- (void)getAndRunJSPatchScript
{
    NSURLSession *urlSession = [NSURLSession sharedSession];
    NSMutableURLRequest *httpRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxxxx/api"]];
    [httpRequest setHTTPMethod:@"POST"];
    NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:httpRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if(!error)
        {
            NSString *targetJSPatchScript = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [JPEngine evaluateScript:targetJSPatchScript];
        }
    }];
    [dataTask resume];
}

然后運行新的程序,點擊changeColor按鈕,行為已經發生了改變:

demoYellow.png

可以看到,圖中的色塊已經變成了黃色,圖中的提示語言也顯示JSPatch代碼進行了執行,而這一個過程當中,native的邏輯代碼并沒有改變。

  • 歸納

上面代碼片段中的getAndRunJSPatchScript函數只是簡單的調用了接口,獲取了腳本,然后執行。更長遠的規劃,服務端完全可以實現一個腳本管理服務,提供一系列結構,將不同App版本,不同運營行為的腳本進行分類管理,App只需要上報自己的App基本信息,然后由服務端來返回要熱更新的腳本,更加的靈活和體系化。

總結

上述內容可以看到JSPatch確實是一個很方便的熱更新庫,但是個人認為在App的開發過程中,大家還是應該把質量管控在開發階段,不可依賴于這樣的熱更新修復,這樣的修復可以用于應急來修補漏網的bug, 但是不建議作為App的主要邏輯開發層,因為這樣的會帶來更多的外部管理App的功能,從某種意義上來說也添加了運營的復雜性, 而且在蘋果主推的未來開發語言swift中,這種OC語言所具有的動態性將會不再存在,以這套原理來運作的JSPatch庫也就無從使用了,但是在當下的實際OC仍然是很多App的主開發語言的時代,這個熱更新修復的庫還是非常非常實用的,至于各位開發者將會對這個庫依賴多深,這就是自己定奪的問題了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容