iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Software Development

從 Fragment 到 Compose — 老 Android App 重寫的架構取捨系列 第 18 篇

Day 18|裝置鎖定與穩定讀值:狀態機與去抖

  • 分享至 

  • xImage
  •  

動筆前先答(兩題)

1.【決策邊界|規格與驗收】 BLE 廣播式磅秤不需要配對,只要在範圍內,App 就收得到它的讀值。所以現場如果有兩台以上的秤,App 會同時收到好幾台的廣播。舊 App 的做法是:鎖定第一台看到的秤,之後整個任務都只認這台的位址。

新版至少有四種選法:

  • (A) 照舊,鎖定第一台看到的秤
  • (B) 鎖定訊號最強的那台
  • (C) 列出看到的秤,讓配送員自己選
  • (D) 記住上次用過的秤,下次優先選它

請寫下:

  • 你預設選哪一種?在什麼條件下會改選另一種?例如:現場通常只有一台秤,還是兩台秤常常並排放?配送員戴手套操作,點清單方便嗎?
  • 任務進行到一半,鎖定的那台秤收不到廣播了,例如沒電或拿遠了。你要等多久才判定它不見了?判定之後,要自動換到另一台、停在原地等它回來,還是請配送員重新選?
  • 把「鎖定」寫成測試人員不看程式碼也能照著驗的驗收條件,至少寫三條。

我的回答:(待補)

2.【預測再驗證|測試與品質】 秤重畫面要在讀值「穩定」後,才讓配送員送出重量。有人提議直接用 debounce 判斷:

@OptIn(FlowPreview::class)
fun Flow<Int>.stableGrams(): Flow<Int> = debounce(1_000)

用一個假的讀值來源測試。磅秤每 100 毫秒廣播一次:先模擬放上貨物,讀值一路爬升;接著有 3 秒讀值不動。

private fun fakeReadings(): Flow<Int> = flow {
    for (g in 0..15_000 step 1_000) { emit(g); delay(100) }   // 放上貨物,讀值爬升
    repeat(30) { emit(15_000); delay(100) }                   // 之後 3 秒讀值不動
}

@Test
fun `stable weight`() = runTest {
    val emitted = mutableListOf<Pair<Long, Int>>()   // (虛擬時間, 克數)
    val job = launch {
        fakeReadings().stableGrams().collect { emitted += currentTime to it }
    }
    advanceUntilIdle()
    job.cancel()
    println(emitted)
}

先寫下預測:emitted 裡有幾筆?各是什麼時間點、什麼值?理由是什麼?然後跑測試驗證。

接著依序改下面三處,每改一處都先預測、再驗證:

  1. 在 fakeReadings() 的 flow { ... } 最後加一行 awaitCancellation(),模擬磅秤一直開著。
  2. 把 stableGrams() 改成 distinctUntilChanged().debounce(1_000)。
  3. 再把「讀值不動」那段改成 15_000 與 15_010 交替出現,模擬秤的雜訊。

最後回答:

  • 你會怎麼定義「穩定」,寫成一條驗收條件?
  • 你的定義要靠哪一個測試案例,才證明得出 debounce 不夠用?

我的回答:(待補)


上一篇
Day 17|硬體抽象:把藍牙磅秤包成 Flow
下一篇
Day 19|硬體與生命週期:掃描綁 Compose lifecycle、權限流程
系列文
從 Fragment 到 Compose — 老 Android App 重寫的架構取捨 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言