一 Fragment中 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) 什么時候執行?
讓我分析一下相關問題:
- 關于 onPause 時的數據更新:
viewLifecycleOwner.lifecycleScope.launch {viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {// 數據收集代碼}
}
repeatOnLifecycle(Lifecycle.State.STARTED)
意味著
只在 Fragment 處于 STARTED 狀態(在 onStart 之后,onPause 之前)時才會收集數據- 當 Fragment 執行 onPause 時,數據收集會暫停,不會更新 UI
- 這是正確的行為,因為不應該在后臺更新不可見的 UI
- 關于 onDestroyView 后的狀態恢復:
private val tempViewModel: TemperatureViewModel by viewModels()
private val fanViewModel: FanViewModel by viewModels()
private val sharedViewModel: SharedViewModel by activityViewModels()
viewModels()
delegate 會在 Fragment 的生命周期內保持 ViewModel 實例activityViewModels()
delegate 會在 Activity 的生命周期內保持 ViewModel 實例- ViewModel 的狀態會在配置更改(如屏幕旋轉)時自動保持
- 即使 Fragment 的 View 被銷毀,ViewModel 仍然存活,保持著最新狀態
- 當 Fragment 重新創建 View 時,會使用同一個 ViewModel 實例,因此狀態自動恢復
- 數據恢復流程:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)initView()collectUiState() // 重新開始收集數據
}
- 當 Fragment 重新創建 View 時,會重新調用 collectUiState()
- ViewModel 中的 StateFlow/Flow 會發送最新狀態
- UI 會立即更新到最新狀態
這種實現方式是合理的,因為:
- 遵循了 Android 生命周期安全原則
- 避免了在后臺不必要的 UI 更新
- 保證了狀態的可靠性和一致性
- 有效處理了配置更改場景
二 為什么viewModel在view銷毀,仍能存活,保存著ui狀態?
delegate
確實是代理的含義。讓我解釋 viewModels()
delegate 的工作原理:
- Delegate 機制:
private val viewModel: MyViewModel by viewModels()
by
關鍵字表示使用 Kotlin 的屬性代理viewModels()
返回一個ViewModelLazy
對象- 代理負責處理 ViewModel 的實例化和獲取
- ViewModelLazy 工作原理:
class ViewModelLazy<VM : ViewModel>(private val viewModelClass: KClass<VM>,private val storeProducer: () -> ViewModelStore,private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {private var cached: VM? = nulloverride val value: VMget() {val viewModel = cachedreturn if (viewModel == null) {val factory = factoryProducer()val store = storeProducer()ViewModelProvider(store, factory).get(viewModelClass.java).also {cached = it}} else {viewModel}}
}
- ViewModel 存活原理:
- Fragment 持有對
ViewModelStore
的引用 ViewModelStore
是一個簡單的 HashMap,用于存儲 ViewModel 實例ViewModelStore
與 Fragment 的生命周期解耦,不隨 View 的銷毀而銷毀- 只有在 Fragment 真正被銷毀時(如 Activity 被銷毀),
ViewModelStore
才會清理
- 配置更改時的行為:
Fragment 重建 ─→ ViewModelStore 保持存活
↓ ↓
新的 View 原有 ViewModel 實例
↓ ↓
重新綁定數據 狀態保持不變
優點:
- 延遲初始化 - 只在首次訪問時創建 ViewModel
- 生命周期安全 - 自動處理 ViewModel 的創建和清理
- 狀態保持 - 在配置更改時保存數據
- 內存管理 - 適時清理不需要的 ViewModel 實例
這就是為什么即使在 View 銷毀或屏幕旋轉時,ViewModel 仍能保持狀態的技術原理。
三 android中 activity和viewModel生命周期的區別是什么
在 Android 的架構中,Activity
和 ViewModel
有著不同的生命周期管理機制,這是 MVVM 架構中實現 UI 狀態持久化和解耦的核心基礎。
? 一、Activity 與 ViewModel 生命周期的區別
對象 | 生命周期誰管理 | 生命周期范圍 | 銷毀時機 |
---|---|---|---|
Activity | 系統管理(與 UI 生命周期強綁定) | 短暫(屏幕旋轉、返回鍵等都會銷毀) | 屏幕旋轉、按返回鍵、系統回收 |
ViewModel | ViewModelStore 管理(由 ViewModelProvider 創建) | 綁定到 Activity / Fragment 的生命周期 | 所依附的 Activity 或 Fragment 銷毀后才銷毀 |
? 二、為什么 Activity 銷毀了 ViewModel 才銷毀?
因為 ViewModel
是為“配置更改下的數據持久化”設計的組件,Android 會:
- 在屏幕旋轉(或語言切換等配置更改)時,銷毀舊的
Activity
實例,創建一個新的。 - 但 ViewModel 被托管在
ViewModelStore
中,不跟隨Activity
的配置變更銷毀。 - 直到 Activity 真正 finish 或被系統 kill,ViewModel 才銷毀。
👉 這意味著:
屏幕旋轉只是臨時銷毀了 UI,但 ViewModel 仍然活著。
? 三、為什么 ViewModel 可以持久化 UI 狀態?
因為 ViewModel 的職責就是存儲并管理界面相關的數據,且生命周期長于 Activity:
- 它存在于
Activity
或Fragment
的作用域內,但 不會因配置更改而銷毀。 - 所以可以在 ViewModel 中存儲 UI 狀態(如輸入框內容、選中項、滾動位置等)。
- 當新的 Activity 創建時,通過同一個
ViewModelProvider
獲取原有 ViewModel,數據依然存在!
🎯 舉個例子:
class MyViewModel : ViewModel() {val userName = MutableLiveData<String>()
}
在 Activity 中使用:
val viewModel = ViewModelProvider(this)[MyViewModel::class.java]viewModel.userName.observe(this) {// 更新 UI
}
即使屏幕旋轉,Activity 重建了,但 viewModel
是同一個,userName
依舊保留!
? 四、深入原理:ViewModel 如何“活得更久”?
- Android Framework 在配置變化時,會:
- 暫時保存
ViewModelStore
和SavedStateRegistry
。 - 銷毀舊 Activity 并創建新 Activity。
- 將舊的
ViewModelStore
重新附加給新 Activity。
- ViewModel 保存在
ViewModelStore
,而不是在 Activity 實例里。
所以:
Activity 重建 ≠ ViewModel 重建
? 五、ViewModel 銷毀時機總結:
Activity.finish()
或被系統 kill,才會銷毀 ViewModel。- Fragment 的 ViewModel 也可以作用域更長(如綁定到父 Activity)。
ViewModel
的onCleared()
方法會在真正銷毀時調用,可釋放資源。
? 六、補充:如果希望屏幕旋轉后也保持 UI 狀態,有三種方式對比:
方法 | 是否推薦 | 說明 |
---|---|---|
onSaveInstanceState() | ? 可選 | 保存輕量級 UI 狀態(如文本、位置) |
ViewModel | ? 推薦 | 保存更大或復雜的數據結構,且與 UI 解耦 |
Room/DB | ? 長期存儲 | 跨進程或跨 Activity 的持久狀態 |
四 viewModel什么時候銷毀?
屏幕旋轉時,Activity 的確會被銷毀,但這并不等價于我們手動調用的 Activity.finish()
方法。這兩者在 Android Framework 中有重要區別。
? 關鍵理解:屏幕旋轉導致的是“配置更改銷毀”,不是調用 finish()
當屏幕旋轉發生時:
系統會執行:
1. 銷毀舊的 Activity 實例(onDestroy() 被調用)
2. 創建一個新的 Activity 實例(走 onCreate())
但這 并不是調用了代碼中的 finish()
方法,而是 Android 框架內部處理配置變化的一個機制。它不會觸發真正的「完全生命周期終結」,而是帶有保留機制的銷毀。
? 為什么 ViewModel 不被銷毀?
背后的機制是:
- 每個
Activity
背后都有一個ViewModelStore
。 - 當因配置變化(如旋轉)導致 Activity 被銷毀時,Android 會臨時保存舊的
ViewModelStore
。 - 然后,新 Activity 被創建時,從保存的
ViewModelStoreOwner
重新獲取舊的 ViewModel 實例。
這個行為是由 ViewModelProvider
和 SavedStateViewModelFactory
等底層機制共同支持的。
? 更形象地理解:
假設旋轉屏幕時發生了如下變化:
舊的 Activity(A1):
ViewModelStore 里存著 MyViewModel旋轉后創建新的 Activity(A2):
系統會把 A1 的 ViewModelStore 傳遞給 A2A2 調用 ViewModelProvider(this)[MyViewModel::class.java] 時,
得到的是 A1 中的同一個 ViewModel 實例
因此:
- ViewModel 沒有被銷毀。
- ViewModel 里的數據依然存在。
- 看到 UI 狀態得以“保留”。
? 那 ViewModel 什么情況下才會銷毀?
ViewModel 生命周期取決于它所綁定的 ViewModelStoreOwner
,比如 Activity
或 Fragment
:
情況 | ViewModel 會銷毀嗎? |
---|---|
屏幕旋轉(配置更改) | ? 不會 |
手動調用 finish() | ? 會 |
用戶點擊返回鍵退出 Activity | ? 會 |
系統因資源不足殺死進程 | ? 會 |
Fragment detach 且不復用 | ? 會 |
? 所以總結:
- 屏幕旋轉 ≠ finish(),雖然
onDestroy()
會被調用,但屬于“可恢復銷毀”。 - ViewModel 被設計為適應這種情況,并保留數據,避免 UI 狀態丟失。
- ViewModel 的生命周期由 ViewModelStore 控制,不直接綁定 Activity 的內存生命周期。
五 Fragment 關聯的 ViewModel 的銷毀時機。
1. Fragment ViewModel 的兩種作用域
Fragment 可以使用兩種方式創建 ViewModel:
// 1. Fragment 作用域的 ViewModel
private val fragmentViewModel: MyViewModel by viewModels()// 2. Activity 作用域的 ViewModel
private val activityViewModel: SharedViewModel by activityViewModels()
2. Fragment作用域 ViewModel 的銷毀時機
Fragment 作用域的 ViewModel 在以下情況會銷毀:
class MyFragment : Fragment() {private val viewModel by viewModels<MyViewModel>()override fun onDestroy() {super.onDestroy()// Fragment 真正銷毀時,ViewModel 也會銷毀}
}
銷毀場景:
- Fragment 被移除且不再復用
- 宿主 Activity 被銷毀(非配置變更導致的銷毀)
- Fragment detach 且不會再次 attach
不會銷毀的場景:
- Fragment 視圖銷毀(onDestroyView)
- 屏幕旋轉等配置變更
- Fragment 被替換但加入了返回棧
3. Activity作用域 ViewModel 的銷毀時機
class MyFragment : Fragment() {private val sharedViewModel by activityViewModels<SharedViewModel>()// Activity 銷毀時才會銷毀,Fragment 的生命周期變化不影響
}
銷毀場景:
- 宿主 Activity finish
- 系統回收 Activity
- 用戶按返回鍵退出 Activity
4. 實際示例
class MyFragment : Fragment() {// Fragment 作用域 ViewModelprivate val fragmentViewModel by viewModels<MyViewModel>()// Activity 作用域 ViewModelprivate val sharedViewModel by activityViewModels<SharedViewModel>()override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {// Fragment view 創建return inflater.inflate(R.layout.fragment_my, container, false)}override fun onDestroyView() {super.onDestroyView()// View 銷毀,ViewModel 仍然存活}override fun onDestroy() {super.onDestroy()// Fragment 銷毀,fragmentViewModel 銷毀// 但 sharedViewModel 仍然存活}
}
5. ViewModel 清理機制
class MyViewModel : ViewModel() {override fun onCleared() {super.onCleared()// ViewModel 銷毀時執行清理工作// 取消協程// 釋放資源}
}
6. 注意事項
- 防止內存泄漏:
class MyViewModel : ViewModel() {private val coroutineScope = CoroutineScope(Dispatchers.Main + Job())override fun onCleared() {coroutineScope.cancel() // 清理協程super.onCleared()}
}
- 視圖引用:
class MyViewModel : ViewModel() {// 錯誤:不要持有 View 引用private lateinit var binding: FragmentBinding// 正確:使用 LiveData/StateFlow 通知 UIprivate val _state = MutableStateFlow<UiState>()val state = _state.asStateFlow()
}
7. 總結
Fragment ViewModel 的銷毀時機取決于:
- 使用的作用域(Fragment 還是 Activity)
- Fragment/Activity 的真實生命周期結束
- 非配置變更導致的銷毀
理解這些銷毀時機對于:
- 正確管理資源
- 避免內存泄漏
- 實現合適的數據共享
非常重要。
六 判斷Fragment是否被銷毀
Fragment 調用 onDestroy()
不一定意味著 Fragment 被永久移除。讓我詳細解釋:
Fragment 的幾種銷毀情況
- 配置變更時的臨時銷毀:
class MyFragment : Fragment() {private val viewModel by viewModels()override fun onDestroy() {super.onDestroy()// 配置變更時會調用,但 ViewModel 不會銷毀}
}
- 真正的永久銷毀:
// Activity 中
supportFragmentManager
.beginTransaction()
.remove(fragment) // 移除 Fragment
.commit()
- 加入返回棧的銷毀:
supportFragmentManager
.beginTransaction()
.replace(R.id.container, NewFragment())
.addToBackStack(null) // Fragment 被替換但可返回
.commit()
判斷是否真正銷毀的方式
class MyFragment : Fragment() {override fun onDestroy() {super.onDestroy()when {// 1. 檢查是否是配置變更activity?.isChangingConfigurations == true -> {// ViewModel 不會銷毀}// 2. 檢查是否在返回棧中isRemoving && !requireActivity().isFinishing -> {if (fragmentManager?.backStackEntryCount ?: 0 > 0) {// Fragment 在返回棧中,ViewModel 不會銷毀} else {// Fragment 被永久移除,ViewModel 會銷毀}}// 3. Activity 正在銷毀requireActivity().isFinishing -> {// Fragment 和 ViewModel 都會銷毀}}}
}
Fragment ViewModel 的銷毀時機
class MyFragment : Fragment() {private val viewModel by viewModels()// ViewModel 會在以下情況銷毀:// 1. Fragment 被永久移除fragmentManager.beginTransaction().remove(this).commit()// 2. 宿主 Activity 被銷毀activity?.finish()// 3. Fragment detach 且不會重新 attachfragmentManager.beginTransaction().detach(this).commit()
}
關鍵判斷條件
override fun onDestroy() {super.onDestroy()val isReallyDestroying = when {// 配置變更不算真正銷毀activity?.isChangingConfigurations == true -> false// Fragment 被移除且不在返回棧中isRemoving && fragmentManager?.backStackEntryCount == 0 -> true// Activity 結束activity?.isFinishing == true -> trueelse -> false}if (isReallyDestroying) {// ViewModel 會在這種情況下銷毀}
}
總結:
onDestroy()
被調用不等于 Fragment 被永久移除- 需要考慮配置變更、返回棧和 Activity 狀態
- ViewModel 只在 Fragment 真正不再使用時才銷毀