開發手記

都2026年了,讓助眠App整夜播放竟然還是這麼難

我只是想讓手機發出電風扇聲,一直響到天亮。到了2026年,這應該算是解決了的問題吧。結果,它占用了我身為開發者意想不到的時間。

我用了很多年White Noise Lite。有廣告,介面在我看來也很老,但大多時候能用,這才重要。後來它開始頻繁停下來。我把這些情況當作當機,但沒有紀錄檔,無法知道真正原因。我也想要現代一點的介面,以及在裝置上產生而非播放錄音的聲音,還能細緻調整。於是我做了Veil,也真正體會到可靠性問題有多難。

iOS的背景播放相對直接,以我自己的經驗,Veil從一開始就很穩定。Android則成了長期工程:硬體五花八門,有記憶體少的舊手機,有廠商各自的電池管理,還有播放途中安裝的更新。我自己的程式碼也有不少問題。對凌晨三點醒來的人來說,這些區別並不重要。

我從當機報告入手,很快發現需要更多脈絡。被終止的程序可能來不及回報例外,存活的程序也可能只有無聲的音訊串流。即便有前景服務和播放通知,也不能證明喇叭真的在響。我加入了原生播放紀錄、程序結束資訊、記憶體測量、服務狀態、音訊串流事件,以及明確的主動停止紀錄。相關證據會在程序結束後保留下來,等下次啟動時收集。

目標是盡量還原一晚的經過,區分可能的原因。當時是否仍應播放?計時器是否結束?記憶體是否增加?手機是否充電?Android是否替換了應用程式套件?更多訊號有幫助,但前提是知道每種訊號到底能說明什麼。

最早需要修正的,其實是測量本身。從最近使用的App滑掉Veil,可能停止了聲音,卻沒正確關閉紀錄中的工作階段。幾小時後Android回收快取程序,我的報告就把它算成播放故障。早晨主動停止,變成了「夜間被終止」。而真正打斷播放的更新,反而被當作正常系統結束排除了。

修正這些定義後,還沒繼續修音訊程式碼,故障樣貌就變了。此後我一直區分播放改善和測量改善。我定期按版本、裝置系列、工作階段長度和結束原因分析,排除測試裝置並去除重複資料。延遲回報也很重要:新版跑過幾段短播放,並不能說明它能撐過一晚。

這些模式引導實機測試。我在實體手機上做長時間和整夜測試,也使用Samsung遠端實驗室,持續擷取logcat、記憶體和記憶體壓力資料,並在故障時儲存音訊與服務狀態。這樣不用我親眼撞見故障,也能取得證據。

測試環境本身也會影響結果。USB會供電,所以連著USB並不是拔掉電源後的電池測試。反覆執行dumpsys meminfo甚至可能引起聲音異常。一晚正常,只能說明在那些條件下沒重現故障,不代表所有裝置和系統狀態都沒問題。

一個意外發現是Android Auto Backup。在我的Pixel上,系統紀錄顯示播放期間BackupManager因備份終止Veil程序。回看整體安裝資料,2026年8月14日至9月11日期間,93個安裝中出現了190次受保護程序的SIGKILL,重複間隔集中在三天左右。這支持備份這個解釋,但不能證明每次結束都由它造成。

我關閉了Auto Backup,接受了雲端還原方面的取捨。Android文件說明備份會關閉App,同時通常排除前景App。正是裝置上的實際紀錄,讓這個具體案例有了可執行的解決方向。Android備份文件

另一些問題在我自己的程式碼裡。舊音訊上下文尚未關閉時,復原邏輯就開始建立新的上下文,在App已經出問題時又疊加了資源。現在會在有限時間內等待清理完成,並將復原與播放操作依序處理。復原也必須尊重更新的使用者意圖:修復排隊時使用者按了停止,就不能修完又把聲音打開。

一次修正還引入了新問題。我加了150毫秒的探測,檢查音訊上下文的時鐘是否前進,以識別那些自稱執行中卻卡住的上下文。但它等待的是JavaScript計時器。在背景,這個等待可能一直卡到App回到前景。現在探測只在前景執行。這也是復原路徑必須在關閉螢幕時測試的原因。

一些最有效的改動其實是產品決定。實機測量顯示,廣告WebView程序關聯著約90 MB額外常駐記憶體。更早的調查也發現過嚴重的SDK相關記憶體故障。我先試著控制負擔,後來乾脆移除了廣告和SDK。對需要整夜執行的App,可靠性更重要。

遙測仍缺少一個關鍵資訊:使用者是否預期聲音會停。發生相關中斷後,Veil可以顯示帶時間的小卡片,問「這次停止在你的預期之內嗎?」這個措辭是有意的。「是你停止的嗎?」會漏掉伴侶停止播放,或本人沒操作但預料到中斷的情況。

如果是預期內的停止,就不把使用者帶去排查電池。如果不是,App會承認中斷,並根據現有證據提供協助。回答直接反映體驗,但不假裝能確定技術原因。也無需讓每位新Android使用者一上來就面對嚇人的權限提示和電池設定尋寶。

這些設定對雙方都麻煩。我可以開啟設定,或在適當時請求豁免,但決定權在使用者手裡。Android的豁免流程也不能控制所有廠商的清理行為。出問題後給一個具體步驟,比第一次播放前就讓人翻陌生設定更有用。Android電源管理指南

更新尤其令人無奈。我可以指引使用者找到Play裡單個App的自動更新設定,但Veil不能控制它。手動更新增加了負擔,Google也為關鍵安全性更新保留例外。一次為了改善夜間穩定性的更新,本身就可能打斷那一晚。Google Play更新設定

更新後的自動復原還沒做完。不只是重建音訊圖,計時器狀態和使用者意圖也必須正確保留。某些廠商特有的模式還需要更好的重現,才能有把握地解釋。我希望App從故障中復原,而不是跟主動停止它的人較勁。

最大的進展來自定期分析、長時間實機測試、針對性修正和直接回饋的結合。每一種都能發現其他方法遺漏的東西。我越來越不在意App是否聲稱正在播放,而更在意有什麼證據。睡前按下播放的人應該能放心相信它,儘管做到這一點比我想的費力得多。

想找適合你手機的具體操作?請看搭配指南:解決Android音訊中斷。