EasyJsWebView 原始碼分析

發表於2016-05-26

 

最近在做hybrid相關的工作,專案中用到了EasyJsWebView,程式碼量不大,一直想分析一下它的具體實現,抽空寫了這篇文章。

1.前言

原生程式碼+h5頁面+甚至React Native(或其他) 的方式開發移動客戶端已經成為當前的主流趨勢,因此老生常談的一個問題就是原生程式碼與js的互動。原生程式碼中執行js程式碼,沒什麼可講的直接webView執行js程式碼即可,本文主要由安卓的js呼叫原生的方式切入,分析iOS端是如何實現類似比較方便的呼叫的。

2.安卓端(js -> native interface)

對安卓的開發不是很熟,只是列舉一個簡單的例子講述這樣一種方式。

  • native端

  • h5端

當h5頁面載入時,onload方法執行,對應的native端中的Contact類中的showcontacts方法被執行。因此核心思想就是通過webView將native原生的類與自定義的js物件關聯,js就可以直接通過這個js物件呼叫它的例項方法。

3.iOS端(js -> native interface)

上述安卓的js呼叫native的方式是如此簡單明瞭,不禁想如果iOS端也有如此實現的話,這樣同時即保證安卓,iOS,h5的統一性也能讓開發者只用關心互動的介面即可。因此便引出了EasyJSWebView的第三方的框架(基於說明2設計),下面從該框架的使用出發,分析框架的具體實現。

說明:

  • 1.iOS端雖然也可以通過JSContext注入全域性的方法但是達不到與安卓端統一
  • 2.iOS端可以通過攔截h5請求的url,通過url的格式區分類或方法,但是這樣不夠直觀,也達不到與安卓端統一

4.EasyJsWebView

4.1 EasyJsWebView使用

本文直接列舉EasyJsWebView Github README例子

  • native端

  • js端

4.2 EasyJsWebView具體實現

4.2.1 EasyJsWebView初始化

初始化設定webView的delegate,實際的webView的回撥的在EasyJSWebViewProxyDelegate中實現,因此我們主要關注EasyJSWebViewProxyDelegate中的webView的回撥的實現即可。

4.2.2 EasyJSWebViewProxyDelegate webView回撥實現

4.2.2.1 webViewDidStartLoad回撥實現

程式碼片段一:

  • 遍歷注入的介面的列表key
  • 通過key獲取注入類的例項
  • 通過類的例項獲取例項方法的列表
  • 依次拼接需要執行js函式的程式碼
  • EasyJS物件的載入,執行EasyJS.inject方法

例子:參考Demo除錯結果如下

4.2.2.2 EasyJS物件

程式碼片段一:

遍歷注入的類的例項方法的列表,通過一個全域性的window[obj]的字典維護對應方法的具體實現。下面我們具體看看EasyJS.call方法的實現。

程式碼片段二:

這段程式碼做了三件事:

  • 1.分別針對引數function型別與其他型別區分處理
  • 2.建立一個IFRAME標籤元素,設定src
  • 3.將新建的IFRAME新增到root元素上

修改IFRAMEsrc預設會觸發webView的回撥的執行,因此便有了下面方法shouldStartLoadWithRequest的攔截。

4.2.2.3 shouldStartLoadWithRequest回撥實現

程式碼片段一:

  • 1.拆分攔截到的requestString拆分為obj,method,formattedArgs三個部分
  • 2.獲取類例項方法的簽名,新建一個NSInvocation例項,指定例項與方法
  • 3.invoker設定引數,然後執行invoke,注意引數中function型別的區分,以下5中會分析回撥function的處理過程。

程式碼片段二:

獲取invoker執行的結果通過webView執行js程式碼返回結果值。

5.EasyJSDataFunction 與 invokeCallback

以下主要分析EasyJsWebView是如何處理回撥方法引數的。

程式碼片段一:

js端call方法這樣處理function引數,EasyJS物件一個全域性的__callbacks字典儲存方法實現物件

程式碼片段二:

native端攔截到請求,執行方法

程式碼片段三:

回撥方法執行,將回撥方法執行引數解析封裝js函式字串,注意前兩個引數第一個表示js函式的唯一ID方便js端找到該函式物件,第二個表示第一次回撥完成是否移除該回撥執行的函式物件的bool值,然後webView主動執行,這樣就完成個整個的回撥過程。

例子:Demo回撥執行語句除錯

6.存在問題

見如下程式碼我們分析實現會發現jsObj全域性字典方法區分的key是方法名的拼接,且去處了連線符號:,因此產生疑問這樣可能還是會出現同一個key對應不同的方法。

鑑於以上的疑問我改了一下Demo工程,MyJSInterface增加一個實現的介面

這樣就會與以下方法衝突

Demo改成如下呼叫

丟擲異常,原因就是js方法全域性字典的keytestWithTwoParamAndParam2所對應的方法被下一個方法覆蓋。

解決:

  • 1.可以儘量避免重名問題
  • 2.也可以替換分隔符號”:”用其他特殊字元替換

本文結,本人還在不斷學習積累中,如果對文章有疑問或者錯誤的描述歡迎提出。
或者你有hybrid iOS一塊比較好的實現也歡迎分享大家一起學習,謝謝!!!

相關文章