成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

原生App與javascript交互之JSBridge接口原理、設(shè)計(jì)與實(shí)現(xiàn)

Lyux / 1475人閱讀

摘要:相關(guān)參考中與之間相互調(diào)用的實(shí)現(xiàn)實(shí)現(xiàn)了與相同的機(jī)制的對(duì)象注入漏洞解決方案存在的意義

前期調(diào)研

調(diào)研對(duì)象:
支付寶,微信,云之家

調(diào)研文檔:
Android中JS與Java的極簡(jiǎn)交互庫(kù) SimpleJavaJsBridge

設(shè)計(jì)需求

閱讀類型的業(yè)務(wù)功能頁(yè)面需要由前端H5實(shí)現(xiàn),需要做到服務(wù)端可控;

頁(yè)面界面更改減少重新發(fā)布新版本的頻率;

功能頁(yè)面部分原型需求無(wú)法實(shí)現(xiàn),需要原生功能支持;

對(duì)未來(lái)業(yè)務(wù)功能的拓展,方便迭代;

作用和意義

定制化JSBridge實(shí)際上是拓展NativeApp的hybrid程度, 參照微信和支付寶,可打造APP強(qiáng)力的生態(tài)圈;

jsBridge在支付,錢包,媒體拓展,圖片處理,活動(dòng)頁(yè)面,用戶地理位置網(wǎng)絡(luò)狀態(tài)都能得到原生強(qiáng)有力支持;

對(duì)于閱讀性頁(yè)面有更多拓展;

優(yōu)秀的通信設(shè)計(jì)方案

前端和Native對(duì)對(duì)方的細(xì)節(jié)知道的越少越好,減少耦合度,暴露的接口盡量控制在5個(gè)以內(nèi);

js與Native之間的通信,最好定義一套通信協(xié)議或者規(guī)則,減少js代碼為兼容不同系統(tǒng)而過(guò)多if;

主動(dòng)發(fā)送消息給對(duì)方時(shí),對(duì)方盡量對(duì)該消息進(jìn)行反饋,即使無(wú)需求對(duì)某些功能做反饋,減少if判斷的兼容代碼;

實(shí)現(xiàn)方式(交互形式) Native 調(diào)用 JS

使用前端暴露在window下的一個(gè)方法或者一個(gè)對(duì)象的方法;
_handlerFromApp(message)
JSBridge._handlerFromApp(message)

方法名: handlerFromApp
參數(shù):

message: {
  cbId  : "cb_(:id)_(:timeStamp)",      //回調(diào)函數(shù)的id
  status: 0,                            //狀態(tài)數(shù)據(jù) (0:失敗, 1:成功)
  msg   : "ok",                         //反饋的消息
  data  : {
    //...                               //一些處理后的數(shù)據(jù)
  } 
}

以下提供的部分參考方法
未對(duì)其進(jìn)行真實(shí)測(cè)試,因?yàn)槲沂褂玫氖莍frame的方法,但原理幾乎相同
建議封裝后提供給Native開(kāi)發(fā)工程師放入對(duì)應(yīng)的APP包中,在webView讀取頁(yè)面的時(shí)候用對(duì)應(yīng)的Native語(yǔ)言注入頁(yè)面,避免頁(yè)面在前端導(dǎo)入被抓取;

var doc = JSBridge || window;
var uniqueId = 1;
var invokeCBMap = {};
var listenCBMap = {};

//
function _send(type, funcName, data, cb) {
  var _id = "cb_" + (uniqueId++) + "_" + new Date().getTime();
  data.cbId = _id;
  if (type == "invoke") 
    invokeCBMap[_id] = cb;
  else if (type == "listen")
    listenCBMap[_id] = cb;
  doc[type](funcName, data);
}
doc._handlerFromApp = function(msg) {
  var _id = msg.cbId,
      callback;
  if (_id) {
    callback = invokeCBMap[_id] || listenCBMap[_id];
    if (callback) {
      delete msg.cbId;
      callback(msg.data);
      delete invokeCBMap[_id];
    } else {
      console.error("不存在該回調(diào)方法");
    }
  }
}
JS調(diào)用Native

以下只介紹前兩個(gè)方法,第三個(gè)和第二個(gè)比較類似

A. Native暴露一個(gè)含有通信方法的類給web調(diào)用

B. Native攔截iframe請(qǐng)求

C. Native攔截prompt彈出框

A 一個(gè)包含調(diào)用方法的類

iOS : 可使用javascriptCore
Android: 直接使用WebView的addJavascriptInterface方法

將一個(gè)js對(duì)象綁定到一個(gè)Native類,在類中實(shí)現(xiàn)相應(yīng)的函數(shù),當(dāng)js需要調(diào)用Native的方法時(shí),只需要直接在js中通過(guò)綁定的對(duì)象調(diào)用相應(yīng)的函數(shù)

確定對(duì)象名稱: (:AppName)JSBridge

Native提供的對(duì)象含有的方法:

invoke(funcName, data)

listen(funcName, data)

invoke:用于web頁(yè)面調(diào)用Native私有方法的通用方法
參數(shù): funcName, data
funcName:對(duì)應(yīng)為Native內(nèi)部私有方法的方法名或映射
data :web傳遞給Native的必要數(shù)據(jù)
data數(shù)據(jù)結(jié)構(gòu)如下:

{
  cbId : "cb_(:id)_(:timeStamp)",  //回調(diào)函數(shù)的id
  msg  : {}                        //提供給使用方法執(zhí)行的一些參數(shù)
}
/** 
  //1.拿wx參考為例
  wx.previewImg({
    current: "http://xxx_1.png",
    urls   : [
      "http: //xxx_0.png",
      "http: //xxx_1.png",
      "http: //xxx_2.png",
      "http: //xxx_3.png",
    ]
  });
  //2.因?yàn)閣x對(duì)jsbridge進(jìn)行了一次封裝,jssdk, 而我們?cè)谖捶庋b時(shí)應(yīng)該如下使用
  JSBridge.invoke("imagePreview", {
    cbId : "cb_(:id)_(:timeStamp)",
    msg : {
      current: "http://xxx_1.png",
      urls   : [
        "http: //xxx_0.png",
        "http: //xxx_1.png",
        "http: //xxx_2.png",
        "http: //xxx_3.png",
      ]
    }
  });
*/

那么當(dāng)調(diào)用之后,Native執(zhí)行完成對(duì)應(yīng)的私有方法后,執(zhí)行一次我們提供的回調(diào)接口,以下是javascript的語(yǔ)法,請(qǐng)Native開(kāi)發(fā)工程師對(duì)應(yīng)修改

JSBridge.handlerFromApp({
  cbId  : "cb_(:id)_(:timeStamp)", //web傳給Native的cbId
  status: 1,                       //狀態(tài)數(shù)據(jù) (0:失敗, 1:成功)
  msg   : "預(yù)覽成功", 
  data  : {} 
});

listen是一個(gè)用于web頁(yè)面監(jiān)聽(tīng)Native方法實(shí)現(xiàn)的通用方法
使用環(huán)境: 不屬于web頁(yè)面上的操作。當(dāng)用戶直接操作Native上的功能來(lái)影響或發(fā)送數(shù)據(jù)給web,或者操作的功能需要用到web頁(yè)面上的數(shù)據(jù),我們需要告知Native我們希望能收到回調(diào);
例子:
微信監(jiān)聽(tīng)分享操作

分享的內(nèi)容是web上的內(nèi)容(標(biāo)題,描述,圖片);

獲取分享操作是否完成和分享操作的數(shù)據(jù)收集;

分享按鈕是原生APP提供;

數(shù)據(jù)結(jié)構(gòu)和操作與invoke相似,對(duì)應(yīng)Native開(kāi)發(fā)哥們接收到listen操作后需要存儲(chǔ)一個(gè)映射,在被監(jiān)聽(tīng)的操作實(shí)現(xiàn)上判斷是不是需要執(zhí)行web端提供的回調(diào)接口;

注意:有關(guān)java addJavascriptInterface的使用有漏洞,詳情見(jiàn)參考第二條鏈接,未驗(yàn)證,僅供讀者自行權(quán)衡;

B iframe的魔法

由于Native App可以監(jiān)聽(tīng)webview的請(qǐng)求,所以web端通過(guò)創(chuàng)建一個(gè)隱藏的iframe,請(qǐng)求商定后的統(tǒng)一協(xié)議來(lái)發(fā)送數(shù)據(jù)給Native App;

function createIframeCall(url) {
  setTimeout(function() {
    var iframe = document.createElement("iframe");
    iframe.style.width = "1px";
    iframe.style.height = "1px";
    iframe.style.display = "none";
    iframe.src = url;
    document.body.appendChild(iframe);
    setTimeout(function() {
        document.body.removeChild(iframe);
    }, 100);
  }, 0);
}

url格式:
(:scheme)://register_type?func=(:funcName)&cbId=(:cbId)&data={...}&verifyTimeStamp=(:new Date().getTime())

scheme:協(xié)議,可用appName,兩端商定,例如weixin,alipayjsbridge

register_type: 注冊(cè)形式,即invoke還是listen

funcName: Native內(nèi)的方法名或映射

cbId:見(jiàn)上文

data:詳細(xì)數(shù)據(jù)

verifyTimeStamp:驗(yàn)證的時(shí)間參數(shù),不必須

;(function() {
    if (window.ZaihuJSBridge) return;
    var CUSTOM_PROTOCOL_SCHEME = "zaihu";
    var REGISTER_INVOKE = "invoke";
    var REGISTER_LISTEN = "listen";
    var uniqueId = 1;
    var invokeCbMap = {};
    var listenCbMap = {};
    function dataHandler(type, funcName, data, cb) {
      var register_type = "";
      switch (type) {
        case "invoke": 
          register_type = REGISTER_INVOKE;break;
        case "listen": 
          register_type = REGISTER_LISTEN;break;
        default: break;
      }
      var cbId = "";
      if (cb) {
        cbId = "cb_" + (uniqueId++) + "_" + new Date().getTime();
        invokeCBMap[cbId] = cb;
      }
      var dataStr = "";
      if (data) dataStr = encodeURIComponent(JSON.stringify(data));
      var paramStr = CUSTOM_PROTOCOL_SCHEME + "://" + register_type + "?func=" + funcName + (cbId ? ("&cbId=" + cbId): "") + (data ? ("&data=" + dataStr): "");
      createIframeCall(paramStr);
        }
    function _invoke(nativeFuncName, data, cb) {
      dataHandler("invoke", nativeFuncName, data, cb);
    }
    
    function _listen(h5FuncName, data, cb) {
      dataHandler("listen", h5FuncName, data, cb);
    }
    function _handlerFromZaihu(msg) {
      var data = JSON.parse(msg);
      var cbId = data.cbId;
      var cb = invokeCBMap[cbId] || listenCBMap[cbId];
      if (cb) {
        delete data.cbId && cb(data) && delete invokeCBMap[cbId];
      }
    }
      var app;
    
      app = {
        version: "0.1",
        invoke: _invoke,
        on: _listen,
        log: _log,
        author: "伊吾魚O(∩_V)O",
        // private
        _handlerFromApp: _handlerFromApp
      };
      window.JSBridge = app;
})()
協(xié)作

需要Native開(kāi)發(fā)兄弟在webview開(kāi)啟時(shí)候?yàn)轫?yè)面注入jsbridge.js代碼并執(zhí)行(防止被前端瀏覽器直接查看源代碼了解app的代碼邏輯)

獲取參數(shù)執(zhí)行對(duì)應(yīng)的功能后,執(zhí)行回調(diào)

頁(yè)面前期準(zhǔn)備

1.app打開(kāi)webview
2.loadUrl(頁(yè)面url)
3.監(jiān)聽(tīng)webview開(kāi)始,并執(zhí)行一段js代碼將包內(nèi)的jsbridge.js文件引入頁(yè)面中;

功能業(yè)務(wù)邏輯

web頁(yè)面調(diào)用請(qǐng)求接口
jsbridge.invoke(funcName, data);(A方法:Native提供,B&C方法: 前端實(shí)現(xiàn));

接口調(diào)用原生功能

原生功能完成后執(zhí)行回調(diào)

比較

A:android曝安全漏洞,但相對(duì)來(lái)說(shuō)實(shí)現(xiàn)簡(jiǎn)單,調(diào)用方式容易,且傳遞參數(shù),無(wú)需前端搭建jsbridge,只需要封裝易用的sdk,App不需要讀取本地靜態(tài)js文件;

B: iframe規(guī)定協(xié)議,規(guī)范統(tǒng)一,需要前端實(shí)現(xiàn)jsbridge和封裝sdk, iframe通過(guò)url的方式,數(shù)據(jù)統(tǒng)一為字符串格式,數(shù)據(jù)量受限制,兩端要轉(zhuǎn)義字符;

C: prompt在一些安卓設(shè)備受系統(tǒng)劫持,監(jiān)聽(tīng)prompt兼容性需要測(cè)試,也是字符串形式,數(shù)據(jù)量不受限,需要轉(zhuǎn)義字符;

還有很多參考頁(yè)面未注明,以及文中有問(wèn)題的地方歡迎提出。

相關(guān)參考
iOS中Objective-C與JavaScript之間相互調(diào)用的實(shí)現(xiàn)(實(shí)現(xiàn)了與Android相同的機(jī)制)
Android WebView的Js對(duì)象注入漏洞解決方案(JSBridge存在的意義)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86758.html

相關(guān)文章

  • 跨平臺(tái)技術(shù)演進(jìn)

    摘要:接下來(lái),我將從原理優(yōu)缺點(diǎn)等方面為大家分享跨平臺(tái)技術(shù)演進(jìn)。小程序年是微信小程序飛速發(fā)展的一年,年,各大廠商快速跟進(jìn),已經(jīng)有了很大的影響力。下面,我們以微信小程序?yàn)槔?,分析小程序的技術(shù)架構(gòu)。 前言 大家好,我是simbawu ,@BooheeFE Team Leader,關(guān)于這篇文章,有問(wèn)題歡迎來(lái)這里討論。 隨著移動(dòng)互聯(lián)網(wǎng)的普及和快速發(fā)展,手機(jī)成了互聯(lián)網(wǎng)行業(yè)最大的流量分發(fā)入口。以及隨著5G...

    魏憲會(huì) 評(píng)論0 收藏0
  • 跨平臺(tái)技術(shù)演進(jìn)

    摘要:接下來(lái),我將從原理優(yōu)缺點(diǎn)等方面為大家分享跨平臺(tái)技術(shù)演進(jìn)。小程序年是微信小程序飛速發(fā)展的一年,年,各大廠商快速跟進(jìn),已經(jīng)有了很大的影響力。下面,我們以微信小程序?yàn)槔?,分析小程序的技術(shù)架構(gòu)。 前言 大家好,我是simbawu ,@BooheeFE Team Leader,關(guān)于這篇文章,有問(wèn)題歡迎來(lái)這里討論。 隨著移動(dòng)互聯(lián)網(wǎng)的普及和快速發(fā)展,手機(jī)成了互聯(lián)網(wǎng)行業(yè)最大的流量分發(fā)入口。以及隨著5G...

    MasonEast 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<