Android JsBridge 原理解析

JsBridge 是 Android 中 WebView 與 Javascript 互相調(diào)用的一個庫瑞躺,github 地址 為 https://github.com/lzyzsd/JsBridge退盯。

本文首先會介紹 JsBridge 中 Javascript 與 Java 通信的原理,其后會使用汰瘫,最后具體介紹 JsBridge 項目的實現(xiàn)原理

一:JsBridge 中 Javascript 與 Java 通信的原理
1.webViewClient.shouldOverrideUrlLoading()

在加載一個新的 url 連接時的一個回調(diào)。目的是給宿主應(yīng)用一個機會是否攔截該新的 url 連接請求渴语。 如果返回 true茵瀑,表示應(yīng)用處理了該 url,返回false 則意味著將 url 請求交給當(dāng)前的 WebView 來處理惜犀。

2. WebView 獲取 Javascript 返回的內(nèi)容

JsBridge 通過重載該方法铛碑,判斷 WebView 加載的 url 中是否包含有與 JS 約定的 schema。Java 端正是通過解析 schema 拿到
Javascript 返回給我們的內(nèi)容

3. Javascript 獲取 WebView 的內(nèi)容

webView.loadUr() 方法可以執(zhí)行 JS代碼虽界。故我們可以通過該方式執(zhí)行 JS 聲明的方法汽烦。傳入我們需要傳遞的數(shù)據(jù)。

比如:‘javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');’

4. Javascript 刷新 webView 頁面

html 中 iframe 元素
iframe 元素會創(chuàng)建包含另外一個文檔的內(nèi)聯(lián)框架
通過設(shè)置 iframe.src = url 來刷新頁面莉御。其中 src 屬性可設(shè)置或返回被載入 iframe 中的文檔的 url撇吞。當(dāng) web 端想要傳遞數(shù)據(jù)給Java 端時俗冻,可以將內(nèi)容序列化包裝成 url 。

二:JsBridge 的使用
1. 添加 gradle 依賴
repositories {
    // ...
    maven { url "https://jitpack.io" }
}

dependencies {
    compile 'com.github.lzyzsd:jsbridge:1.0.4'
}
2. java 提供 Javascript 調(diào)用

webview 注冊 “submitFromWeb” 方法名的 handler

webView.registerHandler("submitFromWeb", new BridgeHandler() {
        @Override
        public void handler(String data, CallBackFunction function) {
            Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
            function.onCallBack("submitFromWeb exe, response data from Java");
        }
    });

Javascript 調(diào)用 submitFromWeb 對應(yīng)的 handler

 WebViewJavascriptBridge.callHandler(
        'submitFromWeb'
        , {'param': str1}
        , function(responseData) {
            document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
        }
    );
3. Javascript 提供 java 調(diào)用

Javascript 注冊 Handler

WebViewJavascriptBridge.registerHandler("functionInJs", function(data, responseCallback) {
        document.getElementById("show").innerHTML = ("data from Java: = " + data);
        var responseData = "Javascript Says Right back aka!";
        responseCallback(responseData);
    });

java 端調(diào)用 Handler

webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
        @Override
        public void onCallBack(String data) {

        }
    });

三:原理分析

tips:下面內(nèi)容是根據(jù) JsBridge 的具體源碼流程來進行分析的牍颈,需要先閱讀源碼迄薄,否則會云里霧里不知道講啥。

準(zhǔn)備工作
  1. WebView 初始化
    WebViewClient中的 onPageFinished() 回調(diào)觸發(fā)時煮岁,webView 加載本地的WebViewJavascriptBridge.js 文件噪奄。

  2. WebViewJavascriptBridge 初始化
    在該 JS 文件中,首先會創(chuàng)建倆個 iframe人乓,然后 docuemnt.createEvent("Events") 創(chuàng)建并發(fā)送一個 WebViewJavascriptBridgeReady 的事件,表明 WebViewJavascriptBridge.js 文件已加載完畢都毒。

  3. demo.html 所做的工作
    該頁面加載時會進行連接 WebViewJavascriptBridge 的任務(wù)色罚。該任務(wù)下會利用 document.addEventListener 監(jiān)聽 WebViewJavascriptBridgeReady 的事件,當(dāng)該事件到來時账劲,取出其中的 webViewJavascriptBridge 對象戳护,調(diào)用 bridge.init() 與 bridge.registerHandler方法。

  4. bridge.init
    該方法是設(shè)置默認(rèn)的 messageHandler (消息處理函數(shù))瀑焦,將其保存在
    webViewJavascriptBridge._messageHandler 中腌且。

  5. bridge.registerHandler 方法則是 Web 端設(shè)置可供Java 端調(diào)用指定方法的處理函數(shù)

java 調(diào)用 JavaScript 流程.
JsBridge.png

上面是 Java 調(diào)用 Javascript 的處理方法的流程圖,接下來根據(jù)序號一一介紹

流程1
BridgeWebView 中使用 callHandler(String handlername, String data, CallBackFunction callback).方法調(diào)用 JS 中的處理函數(shù)榛瓮。 其中 callback 函數(shù)會返回 JS 調(diào)用的結(jié)果铺董。

1.1 doSend 處理
callHandller 實現(xiàn)中調(diào)用 doSend(handlerName, data, callback)函數(shù),函數(shù)中將方法參數(shù)包裝成 Message 對象禀晓,設(shè)置調(diào)用 JS 方法名 message.handleName屬性精续;設(shè)置傳遞數(shù)據(jù) message.data。

為了在 JS 內(nèi)容返回時粹懒,準(zhǔn)確找到回調(diào)函數(shù)重付,這里為回調(diào)函數(shù)分配一個回調(diào) id 標(biāo)識該回調(diào)函數(shù)。 回調(diào) id 的組成由 “JAVA_CALLBACK” + uniqueId +當(dāng)前時間戳凫乖。同時使用
HashMap 將 回調(diào) id 與 回調(diào)函數(shù)聯(lián)系起來确垫。

1.2 queueMessage(Message m)
queueMessage 中又調(diào)用了 dispatchMessage(m)

1.3 dispatchmessage(Message m)
將 Message 對象 json 格式化,然后拼接 url 帽芽,使用 webView.loadUrl 執(zhí)行 JS 代碼删掀。這種的 url 格式為 javascript:WebViewJavascriptBridge._handleMessageFromNative('%s')其中 %s 為 message json 格式化后的數(shù)據(jù)。

流程2
2.1 _handleMessageFromNative(messageJSON);
使用 console.log(messageJSON); 輸出 logcat
接著調(diào)用 _dispatchMessageFromNative(messageJSON)

2.2 _dispatchMessageFromNative(messageJSON)
解析 messageJSON 反序列化為 Message 對象嚣镜,
此時 Message 對象的 callbackId 不為空爬迟,構(gòu)造 responseCallback。查看 messageHandlers 數(shù)組中是否有 message.handlerName對象的處理方法菊匿。有的話即調(diào)用對象函數(shù)付呕。當(dāng)返回結(jié)果為 WebView時计福,調(diào)用該 responseCallback,將結(jié)果返回給 JAVA 端徽职。 該回調(diào)中調(diào)用
_doSend({responseId : callbackResponseId, responseData: responseData}) 函數(shù)象颖。

流程3
3.1 _doSend(message, responseCallback)
這里將發(fā)送的消息保存到 sendMessageQueue.push(message)
messagingIFrame.src = YY://QUEUE_MESSAGE/
iframe.src 方法會刷新當(dāng)前網(wǎng)頁

流程4
4.1 WebViewClient.shouldOverrideUrlLoading(WebView view)
這時候該方法會回調(diào),執(zhí)行到 webView.flushMessageQueue();

流程5
5.1 webView.flushMessageQueue()
執(zhí)行 loadUrl('javascript:WebViewJavascriptBridge._fetchQueue();', CallBackFunction);

5.2 loadUrl
獲取url 中要調(diào)用 JS 端的方法名姆钉,將方法名與回調(diào)函數(shù)保存到 responseCallback

流程6
_fecthQueue()
獲取 JS 端調(diào)用 JAVA 端的消息隊列说订。

流程7
JSON 格式化之后,調(diào)用 bizMEssageIframe.src = YY://return/_fetchQueue/encodeURIConponent(messageQueueString)
該消息隊列的內(nèi)容都是 JS 端要返回給 WebView的消息

流程8
webView.handlerReturnData(url)
同樣 shouldOverrideUrlLoading 方法會回調(diào)會攔截潮瓶,根據(jù)“YY://return”得知這是 JS 端返回的內(nèi)容陶冷,即調(diào)用 webView.handlerReturnData(url)

該函數(shù)內(nèi)首先獲取函數(shù)名 _fetchQueue,獲取之前保存在 responseCallbacks 中的 回調(diào)處理函數(shù)毯辅。調(diào)用函數(shù)埂伦,將 JS 端調(diào)用JAVA 端的消息隊列傳遞過去。

流程9
fetch_queue 的回調(diào)函數(shù)為
獲取消息隊列思恐,遍歷Message沾谜。 其中 messages 中 包含之前 JAVA 端調(diào)用 JS 端,JS處理之后的返回給 JAVA端的回調(diào)函數(shù)胀莹,故其中的 responseId 不為空基跑。 由于 responseId 與 Java 端調(diào)用JS 時 設(shè)置的
CallbackId 是一一對應(yīng)的。 取出其JAVA 設(shè)置的 回調(diào)函數(shù)描焰,調(diào)用該函數(shù)媳否,將 responseData 返回,至此 JAVA 端 調(diào)用 JS 端流程結(jié)束栈顷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逆日,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子萄凤,更是在濱河造成了極大的恐慌室抽,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靡努,死亡現(xiàn)場離奇詭異坪圾,居然都是意外死亡,警方通過查閱死者的電腦和手機惑朦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門兽泄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漾月,你說我怎么就攤上這事病梢。” “怎么了?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵蜓陌,是天一觀的道長觅彰。 經(jīng)常有香客問我,道長钮热,這世上最難降的妖魔是什么填抬? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮隧期,結(jié)果婚禮上飒责,老公的妹妹穿的比我還像新娘。我一直安慰自己仆潮,他們只是感情好宏蛉,可當(dāng)我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著性置,像睡著了一般檐晕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚌讼,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天,我揣著相機與錄音个榕,去河邊找鬼篡石。 笑死,一個胖子當(dāng)著我的面吹牛西采,可吹牛的內(nèi)容都是我干的凰萨。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼械馆,長吁一口氣:“原來是場噩夢啊……” “哼胖眷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霹崎,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤珊搀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后尾菇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體境析,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年派诬,在試婚紗的時候發(fā)現(xiàn)自己被綠了劳淆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡默赂,死狀恐怖沛鸵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缆八,我是刑警寧澤曲掰,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布疾捍,位于F島的核電站,受9級特大地震影響蜈缤,放射性物質(zhì)發(fā)生泄漏拾氓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一底哥、第九天 我趴在偏房一處隱蔽的房頂上張望咙鞍。 院中可真熱鬧,春花似錦趾徽、人聲如沸续滋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疲酌。三九已至,卻和暖如春了袁,著一層夾襖步出監(jiān)牢的瞬間朗恳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工载绿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粥诫,地道東北人。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓崭庸,卻偏偏與公主長得像怀浆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怕享,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容