WebView與APP交互實戰(zhàn)記錄

WebView與APP交互

WebView與APP交互,即網(wǎng)頁通過JSBrige調(diào)用APP的功能,APP也可以通過JSBrige調(diào)用網(wǎng)頁提供的方法。最近剛好接觸到這一塊,記錄一下前端側(cè)的實際操作過程,這篇文章適合還沒接觸過這一塊的同學(xué)們,這里不講原理,直接開始實戰(zhàn)的過程。

準備工作

與客戶端同學(xué)溝通好使用的JSBrige庫,我這里使用的是下面這兩個庫:

iOS(1.1w+ Star): https://github.com/marcuswestin/WebViewJavascriptBridge

Android(6k+ Star): https://github.com/lzyzsd/JsBridge

Star數(shù)量比較高,使用的企業(yè)也比較多,所以還是比較可靠的,可以在它們的文檔中示例代碼。

注意: github上有很多這樣的庫,但最好配套使用,即iOS和Android注入的jsBrige名稱一致,我們前端使用時比較方便統(tǒng)一。

開發(fā)步驟

1. 統(tǒng)一封裝APP注入的JSBrige

ios和android注入的jsbrige可能會有些差異,所以在使用前我們需要抹平不同客戶端的差異。
如果app的同學(xué)使用了上面說的庫,安卓和iOS會在WebView中的window下注入一個WebViewJavascriptBridge的對象,iOS有可能是注入WVJBCallbacks的數(shù)組,也有可能需要通過iframe來注入。iOS的文檔中給出了一個兼容的示例代碼:

function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

我們可以直接使用上面的代碼,為了使用方便,在頁面onload后,我們將這個函數(shù)掛在window下:

window.onload = function () {
  function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
  }
  window.setupWebViewJavascriptBridge = setupWebViewJavascriptBridge
}

window.setupWebViewJavascriptBridge這個函數(shù),就是我們所說的JSBridge,即WebView與APP交互的橋梁。

2. JS調(diào)用APP的方法

APP端的同學(xué)需要先用庫提供的方法在APP端實現(xiàn)一個方法,方法名比如:playMusic(musicId),可以傳遞參數(shù)musicId,表示讓APP開始播放某一首音樂。js調(diào)用時如:

window.setupWebViewJavascriptBridge(function (bridge) {
        bridge.callHandler('playMusic', { musicId: 1 }, function (data) {
          console.log('app觸發(fā)成功了,音樂正在播放。。。APP回調(diào)返回的數(shù)據(jù):', data)
        })
      })

上面相當(dāng)于向app發(fā)起了一個playMusic請求,app收到請求后,執(zhí)行相關(guān)的動作,然后可以callback,網(wǎng)頁可以拿到回調(diào)并繼續(xù)做相關(guān)動作(需要的話)。

3. APP調(diào)JS提供的方法

同樣的道理,若想APP能調(diào)用JS提供的方法,JS中要先注冊這個方法,方法名比如stateChange(state),JS注冊方法:

window.setupWebViewJavascriptBridge(function (bridge) {
  bridge.registerHandler('stateChange', function (data, responseCallback) {
    console.log('收到APP請求stateChange事件,請求的數(shù)據(jù)是:', data)
    // 可以返回給app一個回調(diào)
    responseCallback('朕已經(jīng)收到APP愛卿的請求了,且退下!')
  })
})

到這里,WebView與app的交互其實就完成了,就是這么簡單。

注意事項

1. window.onload時就callHandler可能不成功

一般在Android中會出現(xiàn)這個問題,這是因為APP注入的WebViewJavascriptBridge還不存在,JS就調(diào)用其中的方法了,所以就會沒有效果。而Android的庫也給出了這個注意事項,所以得這樣寫:

window.onload = function () {
  function registerAppEvent () {
    window.setupWebViewJavascriptBridge(function (bridge) {
      bridge.registerHandler('stateChange', function (data, responseCallback) {
        console.log('收到APP請求,請求的數(shù)據(jù)是:', data)
        // 可以返回給app一個回調(diào)
        responseCallback('朕已經(jīng)收到APP愛卿的請求了,且退下!')
      })
    })
  }

  // 兼容寫法 
  if (window.WebViewJavascriptBridge) {
    registerAppEvent()
  } else {
    document.addEventListener(
        'WebViewJavascriptBridgeReady'
        , function() {
            registerAppEvent()
        },
        false
    )
  }
}

但這里沒有考慮到iOS的情況,在最新的iOS中WebViewJavascriptBridge可能是不存在的,所以上面寫法registerAppEvent()在iOS可能無法執(zhí)行。為了避免多次注入事件,我這里用了setTimeout,兼容兩端的流程:

  let isInitBridgeEvent = false // 作為是否已注冊過事件的標記
  if (window.WebViewJavascriptBridge) {
    registerAppEvent()
    isInitBridgeEvent = true
  } else {
    document.addEventListener(
      'WebViewJavascriptBridgeReady',
      function () {
        registerAppEvent()
        isInitBridgeEvent = true
      },
      false
    )
    // 如果還沒注冊則再注冊一次
    setTimeout(() => {
      if (!isInitBridgeEvent) {
        registerAppEvent()
        isInitBridgeEvent = true
      }
    }, 100)

這坨代碼寫的很挫,讓我尷尬癌都犯了,有沒有熱心的小伙伴幫我優(yōu)化下寫法[送花花]。

2. js調(diào)用安卓后callback回調(diào)不成功,js收不到app返回的消息

這個問題其實github上有issue,這是安卓1.0.4版本沒有解決的問題,最新的代碼已經(jīng)解決了。
解決辦法是:安卓需要引入最新的master的代碼,而不要使用1.0.4版本的代碼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文地址 前言 Web 頁面中的 JS 與 iOS Native 如何交互是每個 iOS 猿必須掌握的技能。而 J...
    小嘴冰涼別亂親閱讀 1,994評論 2 5
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,688評論 4 61
  • 1.棧的定義 棧是一種特殊的線性表。其特殊性在于限定插入和刪除數(shù)據(jù)元素的操作只能在線性表的一端進行 結(jié)論:后進先出...
    西西里的姑娘閱讀 565評論 0 0
  • 站在一樓看外面,都是細節(jié),自己附近的那點東西,市井吵架,違章停車。 站在十樓看外面,能看到局部,細節(jié)慢慢消失,輪廓...
    馬首閱讀 1,476評論 0 0
  • 1.關(guān)于寫作資源庫,想了想自己確實引用不到太多。平時看小說更多是為了看情節(jié),幾乎都是一本書讀一遍那種??吹綄懙煤玫?..
    庫小花閱讀 239評論 3 0

友情鏈接更多精彩內(nèi)容