JSPatch使用小記,
hotfix的作用眾所周知,Android和iOS都有各自的技術,但是相比Android的當天發布來說(如果你們的項目不需要灰階),iOS熱更新的意義更加重大。因為iOS審核周期長不說,而且運氣不好會遇到各種被拒,即使申請快速審核,也必須滿足二者之一:能夠準確的告訴蘋果複現crash的步驟,或者在特殊節日附近。 可能你費勁周折的提心弔膽和那麼多天其實也就是在某個類中加三行代碼。
1.簡單介紹
在沒有JSPatch之前,可能有人會使用過JSCocoa。但是有著一系列複雜問題,比如源碼已經多年沒有維護,代碼規模巨大,不支援ARM64。如果想使用還需要升級libffi,並且嘗試相容ARM64,想編譯通過都很困難。
JSPatch的出現基本解決了上述所有問題。在一個項目中接入JSPatch的成本很低,需要動點腦筋的可能就是如何合理的提交和下載。
關於JSPatch的原理作者的部落格已經說的很清楚,本文不再說明,本文主要說的時一些接入操作相關。
如果你不是在董鉑然部落格園看到本文可點擊查看原文。
2.倉庫設定
js檔案肯定不能隨便往後台某個檔案夾一放就讓前端去下載了,雖然使用方便但是在App或者版本較多時容易混亂。建議專門搭建一個遠端倉庫,倉庫裡主要就是檔案夾和js檔案,當需要提交js檔案時,從主幹遷出一個分支,在合適的地方建立檔案夾並添加js檔案,然後給主幹提Pull Request, 這應該是一個麻煩但是規範的流程。檔案夾結構參考:
第三層檔案夾裡,可以用版本名稱也可以使用build號。之後在發請求下載的時候應該是需要拼上項目appname,version等參數。
3.安全性原則
安全相關工作如果沒有做好,最慘的情況是人家可以通過js檔案調用你的任何OC方法,我們肯定不能允許此類事情發生。一般在js檔案提交到倉庫以後後端應該對這一段js代碼進行 md5或者更高手段的編碼,並將這段編碼與檔案存在一起,中得meta.json裡存的就是這一段編碼。 之後在發請求的傳回值的結構應該是大致如下:
{data: {isUpdate: true,content: "require('MTPoiFeedbackM') defineClass('MTFeedbackRankCell',{ setPoiFeedback:function(poiFeedback){ self.ORIGsetPoiFeedback(poiFeedback) var temColor = require('UIColor').lightGrayColor(); self.detailLbl().setTextColor(temColor); }})",code: "9c944f39e57f2e50bdb85deb878cc0f798efb9b0"}}
就是首先有個欄位告訴我們較上次下載的js檔案是否有更新。如果為true再檢測下方返回的code與內容編碼後得到的code是否相同。當然這個內容也可以不直接返回而是返回一個下載的url也是完全可以的。
4.更新頻率
我之前看到很多人把使用js和下載js的代碼都放在了didFinishLaunchingWithOptions:這個方法。我覺得有所不妥,因為如果這個app使用者一直放在手機的後台(比如),並且也沒出現記憶體警告的話,這個方法應該一直不會調用。我建議的是:使用js檔案的代碼放在didFinishLaunchingWithOptions: 而下載js檔案的代碼放在applicationDidBecomeActive: 因為這個方法在程式啟動和後台回到前台時都會調用。並且我建議設定一個間隔時間,根據一些資料和權衡之後我們採用的是間隔時間設為1小時。 也就是說每次來到這個方法時,先要檢測是距離上次發請求的時間間隔是否超過1小時,超過則發請求,否則跳過。
5.接入流程
接入的方式很簡單,作者也提供了Demo程式,大致就分為幾步:
①在General 的 LinkFrameworks and Libraries裡面 添加javascriptcore.framework
這個庫裡主要用於js與oc語言的橋接,比如一些資料類型間的相互轉化。
②podfile添加 pod 'JSPatch' 並pod install
③在代碼中添加使用js和下載js的代碼
這裡作者也給出了樣本,使用和下載
[JPEngine startEngine]; NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"]; NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil]; [JPEngine evaluateScript:script];
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/test.js"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; [JPEngine evaluateScript:script];}];
但是如果你是企業級app,肯定不能直接使用這麼樸素的方式,肯定至少也要封裝一個manager之類的。我們公司的源碼不會在這裡貼出來,但是提供一個建立這個manager大概的思路:
6.JSPatch文法
這裡是作者列出的文法總結:作者原文 不用刻意去看,在寫的過程中逐漸就熟悉了。
下面給出一段範例程式碼解決兩個簡單的bug。
①假設有一個按鈕我們預設應該是讓他不可點擊的,但是之前忘了設定。
OC代碼
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; self.confirm.enabled = NO;}
jspatch
defineClass('MTBRegisterPage',{ viewWillAppear: function(animated) { self.super().viewWillAppear(animated); self.confirm().setEnabled(NO); }})
②假設有一個列表頁有特殊情況臨時想叫我們由黑色改成灰色,並展示一個彈窗
OC代碼
- (void)setDealFeedback:(MTDealFeedbackM *)dealFeedback{ // ------先調用原來的方法,舊代碼保留 self.detailLbl.textColor = [UIColor lightGrayColor]; UIAlertView *temAlertView = [[UIAlertView alloc]initWithTitle:@"提示" message:@"已購物品用灰色展示" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [temAlertView show]; }
jsPatch
require('MTPoiFeedbackM')defineClass('MTFeedbackRankCell',{ setPoiFeedback:function(poiFeedback){ self.ORIGsetPoiFeedback(poiFeedback) var temColor = require('UIColor').lightGrayColor(); self.detailLbl().setTextColor(temColor); var temAlertView = require('UIAlertView').alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles("提示","已購物品用灰色展示", self, "OK", null); temAlertView.show() }})
諸如此類的代碼,覺得在使用的過程中出現頻率最高的bug,就是調用原方法然後加一個判斷用來容錯或return。
也許作者也覺得畢竟這個JSPatch文法 並不是一個正式的語種,大家不會投入太大的精力來仔細學習,所以作者本人也提供了一個簡單粗暴的OC到JS直接轉換地址:http://bang590.github.io/JSPatchConvertor/
這個地址親測一些簡單的寫法是正確轉換的,但是比較複雜的文法還是不能讓機器直接搞定,還是需要人工修改的。作者也在不斷完善這個工具。 這個轉換器的實現原理:http://blog.cnbang.net/tech/2915/
7.更多思考
①.接入了JSPatch之後,iOS的線上BUG 看上去就不向以前那樣“猛如虎”了,但是這僅僅是一個緊急預案措施,以前規範的流程還是需要遵守。
②.每一次本版本用JSPatch解決的線上Bug,下個版本必須用OC代碼寫入項目中,不能允許補丁代碼的存留超過一個版本。
③.倡導使用敏捷開發的思想,類似於主邏輯或者是功能模組入口的方法可以抽的更細,這樣即使需要修改,成本也不會太大,作者本人也提到,如果有一行代碼必須要在一個大方法的中間進行修改,那我也沒辦法了,你只能把這整個方法都用js寫一遍了,所以才設定了JSPatchConvertor。
④.每次用JSPatch解決掉的線上BUG 應當有一個專門的文檔記錄,遇到重複錯誤必須寫casestudy。
暫時想到這些,希望本文能對準備接入JSPatch的開發人員有所協助。
如果你不是在董鉑然部落格園看到本文可點擊查看原文。
----------------------
本人github:https://github.com/dsxNiubility