Hybrid APP 開發(六):JSSDK

三水清發表於2019-03-04

拖稿了好久的「Hybrid APP開發系列」又更新了~
今天繼續寫JSSDK

為什麼會有JSSDK

我之前文章介紹了通過 JSBridge 實現頁面和NA的相互呼叫,並且介紹了模板本地包的開發和後臺維護系統。今天介紹的是JSSDK,通過 JSSDK 可以實現:

  1. 抹平JSBridge的平臺實現差異

  2. 對齊端能力,內部消化版本差異

  3. sdk封裝後的程式碼更加符合前端習慣

  4. 許可權控制、鑑權、對外開放,實現生態建設

關於sdk的程式碼級別的設計,可以參考文章:《JSSDK設計指南

如果做過微信頁面開發的,應該都知道 wx.js,這就是微信的JSSDK,在微信內需要呼叫微信的端能力就需要引入這個js。

JSSDK的設計

JSSDK的設計包括兩部分:

  1. 隨著每個NA客戶端版本內建的js,稱為: inject.js,他的主要作用是封裝JSBridge邏輯,通過隨版更新實現減少端能力的版本分裂,降低整個sdk的程式碼複雜性。 inject.js是一段js程式碼,當客戶端載入一個頁面的時候,由客戶端在適當的時機注入到webview內執行,執行後的程式碼就會有給webview增加js方法,例如微信的 _WeixinJSBridge,類比chrome開發外掛當中的 content_scripts,可以在 document_startdocument_end等時機進行執行。

  2. 雲端JS,即實際暴漏給開發者使用的js,稱為: jssdk.js,這個是真正開發者使用的sdk檔案,通過 script外鏈引入,例如 wx.js,這個js檔案通過和 inject.js進行交換,完成端能力的呼叫、鑑權和客戶端事件監聽等操作

Hybrid APP 開發(六):JSSDK

inject.js 和 jssdk.js工作機制

inject.js是客戶端和 jssdk的「翻譯官」,他接受頁面 jssdk的方法呼叫,將呼叫的命令解析成客戶端可以理解的「語言」(JSBridge)然後傳給客戶端,同時當客戶端有事件/回撥響應的時候,也通過 inject.js進行分發/回撥。

Hybrid APP 開發(六):JSSDK

舉例:NA分享能力

例如,客戶端實現了一個分享皮膚的UI元件,開放給web頁面可以呼叫,這時候需要呼叫NA的端能力,JS需要將分享的:icon_url、title、content、link、type甚至訂製的NA皮膚資訊等傳給NA,NA開始彈出這個皮膚,使用者進入NA層進行互動;

當使用者分享成功、失敗、取消等事件發生的時候,需要回撥JS程式碼,使用者由回到了web頁面,NA回撥了JS callback,JS實現後續的邏輯。

Hybrid APP 開發(六):JSSDK

jsbridge為:

  1. demoapp://share/dialog?title=三水清&link=http://js8.in&
    複製程式碼

  2. icon_url=xxx&content=我發現一個很有用的前端公眾號複製程式碼

inject.js程式碼封裝如下:

  1. ;(function (window, document) {複製程式碼

  2. function invoke (module, action, args, callback) {複製程式碼

  3. let scheme = `demoapp://${module}/${action}?`複製程式碼

  4. if (isFunction(args)) {複製程式碼

  5. callback = args複製程式碼

  6. args = null複製程式碼

  7. }複製程式碼

  8. // 處理下引數複製程式碼

  9. if (isString(args)) {複製程式碼

  10. scheme += args複製程式碼

  11. } else if (isObject(args)) {複製程式碼

  12. each(args, (k, v) => {複製程式碼

  13. if (isObject(v) || isArray(v)) {複製程式碼

  14. v = JSON.stringify(v)複製程式碼

  15. }複製程式碼

  16. scheme += `${k}=${v}`複製程式碼

  17. })複製程式碼

  18. }複製程式碼

  19. // callback獨立傳,方便全域性函式名命名複製程式碼

  20. if (isFunction(callback)) {複製程式碼

  21. var funcName = '_jsbridge_cb_' + getId()複製程式碼

  22. window[funcName] = function () {複製程式碼

  23. callback.apply(window, ([]).slice.call(arguments, 0))複製程式碼

  24. }複製程式碼

  25. scheme += (!~scheme.indexOf('?') ? '&' : '?') + `callback=${funcName}`複製程式碼

  26. }複製程式碼

  27. if (os.ios && versionCompare(os.version, '9.0') >= 0) {複製程式碼

  28. window.location.href = scheme複製程式碼

  29. } else {複製程式碼

  30. var $node = document.createElement('iframe')複製程式碼

  31. $node.style.display = 'none'複製程式碼

  32. $node.src = scheme複製程式碼

  33. var body = document.body || document.getElementsByTagName('body')[0]複製程式碼

  34. body.appendChild($node)複製程式碼

  35. setTimeout(function () {複製程式碼

  36. body.removeChild($node)複製程式碼

  37. $node = null複製程式碼

  38. }, 10)複製程式碼

  39. }複製程式碼

  40. }複製程式碼

  41. var $ = {複製程式碼

  42. share: function (opts, callback) {複製程式碼

  43. var defaultOpts = {複製程式碼

  44. url: location.href,複製程式碼

  45. title: '三水清',複製程式碼

  46. content: '最好的前端公眾號',複製程式碼

  47. icon_url: 'http://baidu.com/icon.png'複製程式碼

  48. }複製程式碼

  49. opts = Object.assign(defaultOpts, opts)複製程式碼

  50. invoke('share', 'dialog', opts, callback)複製程式碼

  51. }複製程式碼

  52. }複製程式碼

  53. window._InjectJS_ = $複製程式碼

  54. }(window, document))複製程式碼

jssdk.in程式碼示例:

  1. window.jssdk = {複製程式碼

  2. share: _InjectJS_.share複製程式碼

  3. }複製程式碼

頁面呼叫:

  1. jssdk.share({url: 'http://js8.in'}, (err, data) => {複製程式碼

  2. if (!err) {複製程式碼

  3. if (data.errno === 1) {複製程式碼

  4. alert('失敗')複製程式碼

  5. }else if (data.errno === 2) {複製程式碼

  6. alert('取消')複製程式碼

  7. }else {複製程式碼

  8. alert(data.media) // 分享的平臺id,比如webxin_timeline複製程式碼

  9. }複製程式碼

  10. }複製程式碼

  11. })複製程式碼

這樣 jssdk呼叫 inject.js寫法,看似多此一舉,實則很巧妙,試想一下下面的場景:

  1. 客戶端某個版本分享能力升級,需要做相容

  2. 某版本分享能力有bug,會引起crash,不能在此版本呼叫

  3. 分享成功之後的回撥需要做鑑權,防止惡意刷分享行為

  4. JSBridge有scheme調起換成jsinterface的調起(參考本系列JSBridge文章)

如果這些程式碼都寫在 jssdk.js,那麼隨著版本的積累,程式碼會越來越臃腫,並且所有版本的端能力都集中在 jssdk.js,很不利於管理,歷史的包袱也甩不掉。

inject.js的注入時機

因為 inject.js的設計機制,所以我們希望 inject.js能夠越早注入越好,這樣我們在頁面head使用 jssdk.js 就不會找不到物件了!

我們知道安卓WebView中可以通過 webview.loadUrl("javascript:xxx”)的方式來呼叫js裡面的程式碼,那麼,我們也可以利用 webview.loadUrl("javascript:xxx”);的方式來載入注入一段 js 程式碼 。

安卓WebView 需要通過 webView.setWebViewClient(new MyWebClient());的方式來監聽網頁載入的各個週期方法回撥,那麼我們只需要在 onPageFinished(WebView view, String url) 中注入提前設定好的js 即可

在iOS中也有對應的時間點: webViewDidFinishLoaddidCreateJavaScriptContext

我們能夠找到的注入時機有限,為了保證jssdk程式碼在呼叫的時候,已經注入成功 inject.js,我們只能實現類似 DOMContentLoaded這樣的 ready方法回撥,使用jssdk的時候,全部寫在 jssdk.ready()內(類似 $(document).ready),當頁面 inject.js注入成功則丟擲 ready事件,然後積累的事件棧依次出棧執行。

總結

本文介紹了hybrid開發中為webview實現一個jssdk,介紹了 inject.js的注入時機, inject.js除了端能力的呼叫,還可以和客戶端實現授權(如:微信的接入授權需要申請appid和token),同時還可以針對所有的調起指令和回撥進行安全校驗,遮蔽非法的呼叫和回撥,本文只實現了最簡單的呼叫,這些高階的設計後面文章有機會再介紹,今天敲完收工搬家過節 :P

相關文章

@三水清
未經允許,請勿轉載。

掘金更新比公眾號晚一週左右。

感覺有用,歡迎關注我的公眾號,最新文章第一時間看到!
Hybrid APP 開發(六):JSSDK

相關文章