🌉 橋的執行緒走位圖解
一次「打中怪物 → 問 Unity 能不能死 → 答案回來」的完整旅程|按 ▶ 看小球怎麼走
🎰 安卓板機(同一個 App 內部,四條執行緒=四張辦公桌)
🟪 網頁 JS
web 遊戲主迴圈
60fps 照跑→
🟨 Unity 主執行緒
你的遊戲迴圈
📥收件匣
照跑→
累計延遲 0 ms
七步總表
- 1網頁 JS:打中怪物!呼叫 AndroidBridge.postMessage(hit_query)——丟出去就繼續跑畫面,先播打擊特效(+0.1ms)
- 2橋接執行緒:安卓的「接線生」在自己桌上執行 App 的接收函式,拿到 JSON 字串(+0.1ms)
- 3Unity 收件匣:紙條放進主執行緒的佇列,等下一幀 Update 撿(平均 +8ms,最多 16ms)
- 4Unity 主執行緒:Update 撿到紙條 → 機率模組擲骰「能不能死」——跟你平常寫 Update 一樣,不需要鎖、不需要多執行緒防護(+0ms)
- 5安卓 UI 執行緒:回覆轉交 UI 桌(因為 evaluateJavascript 規定只能這張桌叫)(+1ms)
- 6網頁 JS:事件迴圈的下一個空檔收到 hit_result(+0~16ms)
- 7演出:怪物死亡在這一幀套用。全程 8~33ms=眨眼的十分之一,玩家無感 ✅
為什麼回程不經過「橋接執行緒」?——因為門有兩扇,各自單向
🚪 進門(Web → Unity)
走 addJavascriptInterface 這扇門。安卓規定:網頁打進來的呼叫,由專設的「接線生桌」(橋接執行緒)接聽——不准網頁直接打擾 UI 主桌。所以進來這一趟會經過 🟦。
🚪 出門(Unity → Web)
走 evaluateJavascript 這扇門。安卓通則:WebView 是 UI 元件,凡是操作 UI 元件都限定「UI 主桌」執行。App 自己是主人,不需要接線生——站到 UI 桌把 JS 字串遞給網頁引擎即可。所以出去這一趟走 🟩、不經過 🟦。
🟩 安卓 UI 執行緒=App 視窗的主人桌(畫原生介面、管理 WebView 這個元件)。它只負責「把 JS 字串遞進網頁引擎的收件匣」,真正執行那段 JS 的仍是 🟪 網頁 JS 執行緒(引擎裡跑網頁程式的桌子)。兩張桌子分屬「App 側」與「網頁引擎側」,遞完就各忙各的。
頻繁查詢會不會耗效能?
每過橋一次的 CPU 成本約 0.1~0.5ms(字串搬運+翻譯)。就算打擊峰值每秒 50 次命中,總開銷 ≈ 每秒 25ms=單顆核心的 2~3%,量級上完全安全。
再加一道保險——同幀合併:橋接層把同一幀內發生的多次命中打包成一包過橋(例如陀螺一次撞進怪堆、5 隻怪同幀被打中=1 次過橋、5 筆查詢)。這樣過橋次數上限被鎖死在每秒 60 次,跟打得多兇無關。
真正會出事的用法是「每幀把所有怪物座標傳過橋」那種大狀態同步——我們的協定從設計上就禁止,橋上只走事件。
三個重點
1. 沒有人停下來等。全程都是「放紙條進對方收件匣」,每張桌子有空的下一刻才處理。紫色(網頁)和黃色(Unity)兩條泳道的小燈一直在閃——代表兩邊的遊戲迴圈從頭到尾照跑 60fps,誰都不會被橋卡住。
2. 為什麼不「同步等答案」?如果網頁 JS 呼叫後站在原地等,紫色那條泳道就停擺=畫面當場凍結。所以協定全部非同步:打擊特效先播,死亡判定等答案回來那一幀才演。
3. 跨桌規則全由套件包掉。「哪個 API 只能哪張桌叫」這些坑,UniWebView Channel Messages+我們的橋接層已封裝,遊戲程式只看到 send() 和 on()。Unity 端:橋的訊息一律在主執行緒處理,機率模組照常寫。
對應規格書:《Unity ↔ Web 串接協定 v0.1》附錄 A。此頁為蔡 2026-07-19 執行緒問題的圖解版。