本次測(cè)試的目標(biāo)是 iOS 8+ 之后提供的 WKWebview 提供的 JS 和 Native 通訊機(jī)制之 messageHandler
宪赶,比較淺顯的探索下它各方面的極限。
測(cè)試 Demo
主要的測(cè)試場(chǎng)景包括
- messageHandler 可傳輸?shù)臄?shù)據(jù)類型有哪些痒玩?
- 以字符串為例淳附,messageHandler 最多可傳輸多少字節(jié)?
- 承接 2蠢古,在不同量級(jí)下的傳輸效果(是否丟失奴曙、速度等)如何?
- 如果是 native 向 WKWebview 傳輸數(shù)據(jù)便瑟,是否有類似的表現(xiàn)缆毁?
- 探索使用 messageHandler 的最佳方法。
測(cè)試方法
- 測(cè)試設(shè)備到涂,iPhone XR 模擬器脊框,Xcode 10.2
- 硬件,MacBook Pro (13-inch, 2016)践啄,Mem 16 GB 2133 MHz LPDDR3,3.1 GHz Intel Core i5
- 測(cè)試流程浇雹。啟動(dòng) App,將 Xcode 切換到 Debug Navigation屿讽,觀察內(nèi)存消耗昭灵;同時(shí)打開 Safari 開發(fā)者工具,切換到 Timing tab 里伐谈,觀察內(nèi)存的占用情況
測(cè)試用例
1. 傳輸數(shù)據(jù)類型
根據(jù) MDN 上對(duì) postMessage 描述烂完,
注意: 在 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)之前, 參數(shù)
message 必須是一個(gè)字符串诵棵。
從 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)開始抠蚣,參數(shù)message被
使用結(jié)構(gòu)化克隆算法進(jìn)行序列化。這意味著您可以將各種各樣的數(shù)據(jù)對(duì)象安全地傳遞到目標(biāo)窗口履澳,而不必自己序列化它們嘶窄。
Supported types (表1)
Object type | Notes |
---|---|
All primitive types | However, not symbols |
Boolean object | |
String object | |
Date | |
RegExp | The lastIndex field is not preserved. |
Blob |
|
File |
|
FileList |
|
ArrayBuffer | |
ArrayBufferView | This basically means all typed arrays like Int32Array etc. |
ImageData |
|
Array | |
Object | This just includes plain objects (e.g. from object literals, FormData objects) |
Map | |
Set |
按照上面的數(shù)據(jù),我嘗試了所有的數(shù)據(jù)類型距贷,包括 FormData柄冲。得出的結(jié)論和 AppleDocumentation 一致,
Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.
對(duì)于 JavaScript 側(cè)忠蝗,支持
Primitive Type, Boolean, String, Date, Array(包括 TypedArray)现横。
哪些不在 表1 中的數(shù)據(jù)類型,在調(diào)用 postMessage 即出錯(cuò),如postMessage(document)
长赞;而哪些在表1中晦攒,但不在
Primitive Type, Boolean, String, Date, Array(包括 TypedArray)
中的數(shù)據(jù),傳到 native 時(shí)得哆,message.body
是個(gè)空對(duì)象 {}
;
對(duì)應(yīng) native 向 JS 傳輸數(shù)據(jù)的格式
只有一種,那就是字符串哟旗,凡是可以用字符串轉(zhuǎn)換的數(shù)據(jù)接口都可以借助字符串來實(shí)現(xiàn)打散贩据、傳輸、重建闸餐。
2. 傳輸數(shù)據(jù)速度饱亮。
我們以字符串為例,使用 for
,字符串?dāng)?shù)組join
等方式來模擬傳輸 1\10\100\1000 M
數(shù)據(jù)試驗(yàn)舍沙。
(詳細(xì)的實(shí)現(xiàn)方式可以參考代碼近上,這種測(cè)試代碼準(zhǔn)確度不高,因?yàn)檠h(huán)和靜態(tài)字符串拂铡、臨時(shí)變量是否主動(dòng)銷毀等都會(huì)影響內(nèi)存占用壹无,所以這里得出來的數(shù)據(jù),在數(shù)據(jù)級(jí)和相對(duì)關(guān)系上可供參考)
下表是通過運(yùn)行我的 demo 跑了 3 次感帅,大概的數(shù)據(jù)統(tǒng)計(jì)斗锭。注意:webview 和 Xcode 里的內(nèi)存占用是獨(dú)立的。
表2失球, messageHandler 向 native 傳輸 數(shù)據(jù)
用例 | webkit | xcode 的內(nèi)存消耗 | 通訊耗時(shí) ms |
---|---|---|---|
1000M | 352M/max, 180M | 3.95G/max 141.M | body.length = 1047552000, Costtime = 16323 |
100M | 375M/max, 82M | 259M/max 166.M | body.length = 104755200, Costtime = 763 |
10M | 102M/max ,73M | 79.2M/max 57.M | body.length = 10475520, Costtime = 79 |
1M | 73.58/max ,73.58m | 61.1/max 57.8M | body.length = 1047552, Costtime = 12 |
352M/max, 180M 表示峰值是 252M岖是,之后維持在 180 M
表3, native 向 messageHandler 傳輸 數(shù)據(jù)
同時(shí)实苞,我還測(cè)試了 native 執(zhí)行 webview evaluateJavaScript
來傳數(shù)據(jù)的情況豺撑。
用例 | webkit 瀏覽器 | xcode 的內(nèi)存消耗 | 通訊耗時(shí) ms |
---|---|---|---|
1000M | 3.98G/max 3.0G | 3.98G/max 62.6.M | total = 1047552000, CostTime = 15733 |
100M | 350.51M/max 350.51M | 261M/max 60.4.M | total = 104755200, CostTime = 1307 |
10M | 83.62M/max 83.33M | 81.7.2M/max 60.9.M | total = 10475520, CostTime = 129 |
1M | 53.53/max 51.56m | 62.1/max 59.7M | total = 1047552, CostTime = 16 |
從表2、表3對(duì)比可見黔牵,對(duì)于 Xcode 而已聪轿,內(nèi)存占用是穩(wěn)定的,而對(duì)于 JS 端荧止,容易出現(xiàn)內(nèi)存泄漏屹电。從這點(diǎn)可見 iOS 的 ARC 的優(yōu)點(diǎn)。
3.可能有好奇的寶寶會(huì)追問跃巡,那到底到底有沒有最大值呢危号?
答:都沒有。
- h5 到 native 傳輸?shù)那闆r素邪,在我的測(cè)試用例中外莲,我用人肉和 2 分法(見代碼 index.html ),
[Log] 2149581562.5 is good, try hard. (index.html, line 67)
[Log] 2149581718.75 is good, try hard. (index.html, line 67)
[Log] 2149581796.875 is good, try hard. (index.html, line 67)
[Log] 2149581835.9375 is too much cost (index.html, line 64)
[Log] 2149581816.40625 is good, try hard. (index.html, line 67)
[Log] 2149581826.171875 is too much cost (index.html, line 64)
[Log] The choosed one is 2149581826.171875 (index.html, line 72)
測(cè)試出來 2G 是 messageHandler 傳輸?shù)臉O限。如果傳大于 2G的偷线,則會(huì)出現(xiàn) Out of Memory
的錯(cuò)誤磨确。但我相信這是 JS 方法,
array.join('')
的極限声邦,如果是使用 File
對(duì)象獲取的大于 2G 的文件我相信也是可以傳輸?shù)摹?/p>
-
native 傳輸 h5情況乏奥,基本沒有限制,傳入 10000M 的時(shí)候亥曹,大概執(zhí)行了 5 分鐘邓了,內(nèi)存也飆到 11 G,但兩端都沒有出現(xiàn)問題媳瞪。
傳 10000 M 時(shí)的內(nèi)存占用
理論上是無限的骗炉,現(xiàn)實(shí)是有限的,真正確定上限的是你的硬件蛇受,如我的電腦上執(zhí)行 10000 M 情況時(shí)句葵,已經(jīng)報(bào)警了,再超就會(huì)被殺進(jìn)程了兢仰。
5. 最佳參數(shù)
2 ~ 10 M乍丈,傳輸速度很快,可以傳 1M+ 的數(shù)據(jù)旨别,這時(shí)候的耗時(shí)也很小诗赌。對(duì)于 messageHandler 這么好的通道,還可以發(fā)掘的用途還很多秸弛,我想到的铭若,
- 可以將整個(gè)界面截圖(注意是滾動(dòng)截圖)獲得的數(shù)據(jù)傳到 native,讓 native 持久化递览。
- 保存整個(gè)渲染完畢的 html 結(jié)構(gòu)叼屠,這個(gè)功能可作為個(gè)性化的渲染結(jié)果,代替使用 phatom 這種無頭瀏覽器渲染獲得渲染結(jié)果的方式绞铃。
6. messageHandler 最多可以注冊(cè)多少個(gè)镜雨?
我相信是無數(shù)個(gè),因?yàn)闄C(jī)器配置有點(diǎn)差儿捧,就不再嘗試讓它包內(nèi)存警告了荚坞。window.webkit.messageHandlers
對(duì)象是延遲初始化,在內(nèi)存支持的情況下菲盾,會(huì)自然擴(kuò)展颓影。
結(jié)論
-
native 調(diào)用 h5 傳數(shù)據(jù), JS 的入?yún)ⅲㄈ?Index.html 文件中的懒鉴,_content)诡挂,因?yàn)槭潜豢邕M(jìn)程持有碎浇,會(huì)造成泄漏,即使是即將 _content 置為 null璃俗,這個(gè)情況很嚴(yán)重奴璃,說明 webview evaluateJavaScript 的參數(shù)不能很長,不知如何化解這個(gè)問題城豁?
內(nèi)部對(duì)象持有的 最佳參數(shù)是 2 ~ 10 M苟穆,所以如果大于 10 M 的數(shù)據(jù),可以考慮使用斷點(diǎn)續(xù)傳的方式钮蛛,這時(shí)候需要考慮網(wǎng)絡(luò)傳輸?shù)臋C(jī)制鞭缭,保證時(shí)效(ack_seq)正確和不丟失。
傳 100M 左右的數(shù)據(jù)很慢魏颓,會(huì)卡住主線程。我們的好朋友
worker.js
這時(shí)候不能執(zhí)行postMessage
方法吱晒,也幫不了忙甸饱。但是可以在worker.js
里做大數(shù)據(jù) slice 的工作。window.webkit.messageHandlers.postMessage
應(yīng)該是window.postMessage
的封裝[ 從黑盒測(cè)試的角度來看仑濒,需要看源碼確認(rèn)]叹话。所以他可以很好的實(shí)現(xiàn) frame 和 iframe 之間的跨域問題。例如WebViewJavascriptBridge
因?yàn)樗昧?iframe 的_fetchQueue
墩瞳,導(dǎo)致WebViewJavascriptBridge
在 iframe 是失效的驼壶。這也是 messageHandlers 的長處之一。messageHandler 傳不了 Blob 或者 FormData 等二進(jìn)制文件