R Roam 取得 Roam

在 iOS 上背景記錄 GPS,而不會把這趟路弄丟

iOS 可以在任何一個時刻把你的記錄 App 凍結或直接終止,而且沒有義務先通知你。這對一個「負責把你去過哪裡寫下來」的 App 意味著什麼,以及真正保護得了一趟路的到底是什麼。

你按下開始記錄,把手機放進口袋,騎了三個小時,拿出來一看,App 顯示二十分鐘。這是手機類記錄工具最常見的一種嚴重抱怨,而原因幾乎從來都不是 GPS。

iOS 實際上正在對你的 App 做什麼

一個在背景的 iOS App,不是單純變慢而已。它會在幾種狀態之間移動:還在執行、被凍結(留在記憶體裡,但一行程式都不跑)、以及被終止(整個沒了)。系統依據記憶體壓力、機身溫度、電量,以及它自己的判斷,把你在這幾種狀態之間搬來搬去,而且沒有義務事先告訴你。

宣告了定位背景模式的 App,可以在定位更新持續進來的期間繼續執行,這整件事才有可能成立。比起一般的 App,這已經是一個很強的保證了。但它不是絕對的:在真正的記憶體壓力下——一款大型遊戲、一個相機 App、一支記憶體比較小的手機——你的記錄工具還是可能在半路被砍掉。

這就帶出真正要命的那個失敗。如果 App 是把整趟路握在記憶體裡,等著你按下停止才寫下任何東西,那麼上一次存檔之後的一切就沒了。不是壞掉,是沒了,而且不會有任何錯誤訊息,因為知道這件事的那個行程已經不存在了。

唯一真正的保護:一到就寫下來

這件事沒有什麼聰明的繞法。保護的方式,是讓「還沒存檔的資料」從一開始就不存在。

在 Roam 裡,一次定位回呼就是一個批次,就是一個資料庫交易,而這些點和這趟路的累計數字是一起提交的。沒有一個等著騎完才寫的緩衝區,寫入端也沒有任何一條路徑會安靜地丟掉一個點——一個沒辦法提交的批次會拋出錯誤、被掛進警示裡,而不是被默默吞掉。如果 App 在第四十公里被砍掉,第四十公里已經在手機上了。

把累計數字和點放在同一個交易裡提交,這件事比它看起來重要。它代表摘要那一列,永遠不可能描述一組並不在磁碟上的點,所以被砍掉之後,不會有一個兩邊對不起來的空窗——而且復原也不必重播兩萬八千個點才算得出你當時在哪裡,它讀那一列就好。

同一個想法的另一半是:任何東西都不會被刪除。過濾、平滑化和自動暫停,從來不會把某一列從資料庫裡拿掉。它們是記在點上的標記,而原始的資料流就照它抵達時的樣子留著,這樣當某個啟發式規則對一趟真實的路判斷錯了,原始樣本還在,可以重新推導一次。一個把自己不認同的樣本悄悄丟掉的 App,等於把「它判斷錯了」這件事的證據也一起銷毀了。

被終止之後,怎麼再站起來

如果你給的是「永遠允許」定位,iOS 可以在發生顯著位置變化的時候,把一個已經被終止的 App 在背景重新啟動。這是一條真正的救命索,而它同時帶著一個陷阱,很多記錄工具都栽在上面。

在那次重新啟動裡,App 只有一個很短、而且長度不可預測的視窗,之後系統隨時可能再把它凍結——關鍵是,它可以在任何一個暫停點被凍結,而在現代 Swift 裡,那意味著任何一個 await。一個重新啟動之後先非同步載入一些狀態、然後才去重建定位管理器的 App,可能在中途就被凍住,而一個在重新武裝之前就被凍結的行程,從此再也不會收到 GPS 的任何消息。它看起來還在跑,實際上什麼都收不到。

Roam 的重新武裝刻意寫成同步的,就是為了這個理由:重建定位管理器和重新啟動更新,都發生在第一個 await 之前,讓這個錯誤變成寫不出來,而不只是「不建議這樣寫」。它同時也被寫成可以安全地呼叫兩次,因為背景重啟和前景啟動撞在一起的時候,做的正好就是這件事。

有件事要講清楚:「永遠允許」是保險,不是必要條件。用「使用 App 期間」一樣記錄得好好的,而 App 在權限說明裡就是這樣寫的,不會暗示你非交出那個更強的權限不可。

而且它是保險,不是保證,這個區別值得堅持。Roam 自己的設定裡把這個假設寫成了白紙黑字:它可能永遠不會被重新啟動,而耐久性是它唯一給得起的承諾。iOS 裡沒有任何機制,強制系統把一個被終止的 App 帶回來。從被砍掉到被重新啟動之間發生的事,沒有任何人記錄下來,事後也沒有任何 API 能把它交出來,所以你拿回來的,是被砍之前的那一段,加上重啟之後的那一段,中間是一個誠實的洞。任何把這件事描述成「無縫接續」的 App,描述的是這個平台並不提供的東西。

騎車的人可以做什麼

  1. 只要要記錄比較長的行程,就把定位給「永遠允許」。它決定了一次終止是完全不痛,還是賠掉剩下的半趟。
  2. 能不開低耗電模式就不要開。它會讓背景工作更容易被砍掉。
  3. 記錄途中不要從多工畫面把 App 滑掉。強制關閉等於告訴 iOS 你是認真的,之後不會有任何背景重啟。
  4. 不要同時跑兩個記錄 App。兩個 App 一起把 GPS 撐著,耗電加倍,把其中一個弄死的那種記憶體壓力也加倍。
  5. iOS 更新之後回頭檢查一下權限。系統升級偶爾會把「永遠允許」降成「使用 App 期間」,而且不太聲張。

怎麼判斷你的記錄 App 安不安全

這件事十分鐘就測得出來,不需要任何特殊工具。開始記錄,走五分鐘,然後從多工畫面把 App 強制關閉,再重新打開。如果那五分鐘還在,這個 App 是邊走邊寫的。如果那一趟是空的、或者被截斷了,它就是把你的資料握在記憶體裡,而你現在也很清楚,一趟長程你可能會賠掉多少。

一個記錄工具也應該盡可能少宣告東西。每多一項背景模式,就多一個「可能因為濫用而被終止」的理由,也多一件 App Store 審查人員會來問的事;Roam 只宣告一項——定位——而專案裡有一道建置關卡,只要這份清單多出一筆就讓建置失敗。

常見問題

我的 iPhone 為什麼在背景停止記錄了?

幾乎都是因為 iOS 在記憶體壓力下把 App 凍結或終止了,或者它被人從多工畫面強制關閉。定位背景模式能讓記錄工具活得比一般 App 久很多,但它不是絕對的保證,所以 App 拿到每一個定位點之後做了什麼,比它有沒有活著更重要。

記錄一趟路,App 一定要「永遠允許」定位嗎?

不用。「使用 App 期間」就足以在螢幕關閉的狀態下記錄,因為定位背景模式會在更新持續進來的期間讓 App 保持執行。「永遠允許」多出來的,是讓 iOS 在 App 被終止之後有機會把它在背景重新啟動,所以它是長程的保險,不是必要條件。

背景 GPS 很耗電嗎?

它確實會用掉實實在在的電——接收器持續運作,App 每收到一個定位就醒來一次。現在的手機應付幾個小時的行程綽綽有餘;一整天的活動才是它變成一個問題的地方,也正是在那裡,一個邊走邊寫的 App 決定了你是只損失最後幾分鐘,還是損失一整天。

我要怎麼測試我的記錄 App 會不會掉資料?

開始記錄,走五分鐘,從多工畫面把 App 強制關閉,再打開它。如果那五分鐘活下來了,這個 App 是邊走邊寫的。如果沒有,它就是把你的紀錄留在記憶體裡。

一個不會把這趟弄丟的記錄 App

Roam 每收到一個定位點就立刻寫進手機,和當下的累計數字在同一個交易裡落地;訊號斷掉的那一段就標成斷的,不拿一條直線糊過去;爬升讀的是氣壓計,不是 GPS 高度。沒有帳號、不用登入、不做同步。完整記錄一趟、看它、回放它,都是免費的。

在 App Store 下載

相關文章

全部文章