iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Software Development

AI 時代的 Android Engineer:30 天打造我的 AI 開發工作流系列 第 16 篇

Day 16 - 畫面重建、Process Death、App被關掉,其實不是同一件事

  • 分享至 

  • xImage
  •  

昨天整理 ViewModel 時提到一個很重要的差異:

Configuration Change
≠
Process Death

平常開發其實很常遇到「狀態要不要保留」這個問題,但如果只用一句:

畫面重建後資料要保留

其實不太夠。

因為 Android 裡的「消失再回來」有很多種情況,而不同情況下,還活著的東西完全不一樣。

所以今天想把這件事拆開來看。

第一種:只是 View 被重建

先從 Fragment 開始。

Fragment 的生命週期和它的 View 生命週期其實不是完全一樣的。

例如使用 ViewBinding 時,很常看到:

private var _binding: FragmentDayBinding? = null

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

為什麼 onDestroyView() 要把 binding 清掉?

因為:

Fragment 還存在,不代表它原本的 View 還存在。

也就是可能出現:

Fragment 還活著
↓
View 被銷毀
↓
之後重新建立 View

所以如果把某個重要狀態只存在 TextView:

TextView 顯示 00:30

View 被重建後,新的 TextView 本身並不知道之前是 00:30。

這也是為什麼 UI 最好是:

State
↓
Render UI

而不是反過來把 UI 當成狀態本身。

TextView 顯示什麼應該是結果,不是真實資料唯一存在的地方。

第二種:Configuration Change

再來是很常拿來說明 ViewModel 的例子:旋轉螢幕。

假設 Activity 因為 Configuration Change 被重新建立:

Old Activity
↓
destroy

ViewModel
↓
保留

New Activity
↓
重新取得同一個 ViewModel

這時候如果資料存在 ViewModel,就可以繼續使用。

所以像:

class SearchViewModel : ViewModel() {
    val keyword = MutableLiveData<String>()
}

使用者輸入的 keyword 不會因為 Activity 單純重新建立就一定消失。

這也是 ViewModel 很重要的用途之一。

但這裡要特別注意:

ViewModel 能撐過 Configuration Change,不代表它是永久保存資料的地方。

第三種:Process Death

Android App 執行時,本質上還是在一個 Process 裡。

ViewModel、Singleton、一般 Kotlin Object,這些東西都存在記憶體裡。

所以可能出現:

App 到背景
↓
系統需要回收資源
↓
Process 被殺掉
↓
記憶體裡的物件一起消失

這時:

ViewModel
Singleton
LiveData
一般變數

都不能因為「它原本活得很久」,就期待它還存在。

這點放回前幾天看的 TimeManager 就很有意思。

TimeManager 是 Singleton,所以在同一個 Process 還活著時:

Pomodoro
↓
TimeManager
↑
Stopwatch

大家可以取得同一個 instance。

但:

Singleton 是 Process 內只有一份,不是跨 Process Death 永遠存在。

如果 Process 沒了,Singleton 裡面的記憶體狀態一樣沒了。

所以「做成 Singleton」和「狀態可以被恢復」完全是兩回事。

那 SavedStateHandle 解決什麼?

這時就會輪到 SavedStateHandle。

它適合處理的是:

Process 被系統回收後,如果使用者回到這個畫面,有些必要的 UI 狀態希望可以恢復。

例如一個搜尋畫面:

keyword = "Android"
filter = "Newest"
selectedCategory = 3

這些資料體積不大,而且是重新建立畫面時很有價值的資訊。

就可以考慮:

class SearchViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    var keyword: String?
        get() = savedStateHandle["keyword"]
        set(value) {
            savedStateHandle["keyword"] = value
        }
}

如果 Process Death 後系統重新建立這個 destination,ViewModel 可以透過 SavedStateHandle 拿回之前保存的資料。

但這裡很容易又走到另一個極端:

那我把整個畫面的資料都放 SavedStateHandle 不就好了?

其實也不太對。

能恢復狀態,不代表要保存所有資料

假設一個畫面的資料是:

搜尋條件
↓
呼叫 API
↓
取得 100 筆商品
↓
顯示畫面

Process Death 後,不一定需要保存完整 100 筆商品。

有時候真正需要的是:

搜尋條件

然後:

Process 被重新建立
↓
取得搜尋條件
↓
重新呼叫 API
↓
重新取得資料

這時候保存的是:

重新建立畫面所需要的最小資訊。

而不是把記憶體裡所有東西完整複製一份。

這個概念其實跟資料來源有關。

如果資料原本就能從:

Database
API
Repository

重新取得,那 ViewModel 或 SavedStateHandle 不一定需要自己永久保存完整資料。

再來是「使用者真的把 App 關掉」

這又跟 Process Death 不完全一樣。

如果使用者已經完成一筆番茄鐘紀錄:

Pomodoro 完成
↓
建立紀錄
↓
Room

那這筆資料的需求就是:

明天重新打開 App,我還是要看到。

這時候不管是:

ViewModel
SavedStateHandle
Singleton

都不是最適合的最終保存位置。

它應該進真正的持久化儲存,例如 Room。

所以幾種情況放在一起看,大概會變成:

View 被重建
→ State 重新 Render UI

Configuration Change
→ ViewModel 可以保留狀態

Process Death
→ 必要的狀態需要可以恢復

App 下次重新開啟仍要存在
→ Persistent Storage

這幾層解決的是不同問題。

但 Timer 又比一般 UI State 更麻煩

如果回到現在這個 Project,Timer 還有另一個值得思考的地方。

假設 Stopwatch 已經跑了:

00:37:42

如果只是不斷:

elapsedMillis += 1000

那 Process 一旦出問題,原本累加出來的數字就很難恢復。

但如果真正保存的是:

startedAt = 某個時間點

那重新建立之後就可以:

現在時間 - startedAt
↓
重新算出 elapsed time

這兩種設計代表的恢復能力其實差很多。

保存「現在累加到多少」
vs
保存「計時從什麼時候開始」

前者比較像保存結果。

後者保存的是可以重新推導結果的資訊。

這也是狀態設計裡另一個我覺得很重要的問題:

這個 State 是真正的 Source of Truth,還是其實可以從其他資料算出來?

如果 elapsedMillis 可以由開始時間推導,那真正需要保護的也許不是每一次更新後的 elapsed,而是足以恢復 Timer 的關鍵資訊。

所以「狀態要不要保存」其實還可以再拆

現在如果遇到一個新的 State,我覺得至少可以先問:

這只是 View 被重建嗎?
↓
Activity / Fragment 被重建嗎?
↓
Process Death 後也要恢復嗎?
↓
App 下次開啟還要存在嗎?

然後再問另一組:

這份資料可以重新取得嗎?

可以重新計算嗎?

還是它一旦消失就真的找不回來?

例如:

TextView 現在顯示什麼
→ 可以重新 Render

API Response
→ 可能可以重新 Request

elapsedMillis
→ 可能可以重新計算

使用者已完成的計時紀錄
→ 應該真正保存

這樣再回頭決定要放:

UI
ViewModel
SavedStateHandle
Repository
Room

會比看到 State 就先選一個容器來得合理。

我現在比較想先想「怎麼活下來」

這幾天從 Singleton、ViewModel 一路看到 Process Death,我覺得它們其實一直在回答同一個問題:

這份 State 到底需要活過哪些事情?

如果只需要活過 View 重建,和需要活過 Process Death,設計當然不會一樣。

而如果連 App 下次重新開啟都要存在,那又是另一個層級。

所以與其先問:

這個 State 要用 LiveData 還是 StateFlow?

我現在更想先問:

它是誰的 State?
↓
它需要活多久?
↓
它消失後能不能重新取得?
↓
真正需要保存的是它本身,還是足以重新推導它的資料?

把這幾題回答完,後面的技術選擇反而會清楚很多。

State management 不只是怎麼把資料送到 UI,更重要的是,在 Android 不同生命週期變化下,哪些資料應該留下來,哪些其實可以重新建立。

下一篇見:)


上一篇
Day 15 - ViewModel可以保存狀態,所以什麼都放進ViewModel嗎?
下一篇
Day 17 - Process Death之後,真的要把整個畫面State存起來嗎?
系列文
AI 時代的 Android Engineer:30 天打造我的 AI 開發工作流 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言