ViewModel - 基本使用及底层原理 (一)

456 阅读17分钟

ViewModel的基本使用

1. ViewModel 的基本工作方式

// ViewModelStore 就像餐厅的管理系统
class ViewModelStore {
    // 存储所有 ViewModel,像是员工管理系统
    private val map = mutableMapOf<String, ViewModel>()
    
    // 添加 ViewModel
    fun put(key: String, viewModel: ViewModel) {
        map[key] = viewModel
    }
    
    // 获取 ViewModel
    fun get(key: String): ViewModel? = map[key]
    
    // 清理所有 ViewModel,像是下班时的清场
    fun clear() {
        map.values.forEach { it.clear() }
        map.clear()
    }
}

就像:

  • 餐厅有个管理系统(ViewModelStore)
  • 记录所有在职员工(ViewModel)
  • 即使换班(屏幕旋转),员工还在工作
  • 只有餐厅关门(Activity 销毁)才会清理

2. ViewModel 的创建过程

// ViewModelProvider 就像是人事部门
class ViewModelProvider(
    private val store: ViewModelStore,
    private val factory: Factory
) {
    fun get(key: String): ViewModel {
        // 1. 先查找是否已存在
        var viewModel = store.get(key)
        
        if (viewModel == null) {
            // 2. 不存在则创建新的
            viewModel = factory.create()
            // 3. 存储起来
            store.put(key, viewModel)
        }
        
        return viewModel
    }
}

就像:

  • 需要员工时先查看在职名单
  • 如果没有才招新人
  • 招来的人会登记在系统中

3. 实际工作流程

graph TD
    A[Activity/Fragment] -->|持有| B[ViewModelStore]
    B -->|存储| C[ViewModel]
    D[配置变化] -->|保持| B
    E[Activity销毁] -->|清理| B

4. 具体实现示例

// 1. Activity 中的实现
class MainActivity : ComponentActivity() {
    // ViewModelStore 由 ComponentActivity 持有
    private val viewModelStore = ViewModelStore()
    
    private val viewModel by viewModels<MyViewModel> {
        // 创建 ViewModel 的工厂
        ViewModelProvider.Factory {
            MyViewModel()
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ViewModel 已经准备好了
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 只有在非配置变化导致的销毁才清理
        if (!isChangingConfigurations) {
            viewModelStore.clear()
        }
    }
}

5. 保存状态的机制

class MyViewModel : ViewModel() {
    // 1. 普通数据:会在配置变化时保持
    private val _data = MutableStateFlow<Data>(InitialData)
    
    // 2. 保存状态:会在进程死亡时保存
    private val _savedState = SavedStateHandle()
    
    init {
        // 在 ViewModel 创建时初始化
        viewModelScope.launch {
            loadData()
        }
    }
    
    // 3. 清理资源
    override fun onCleared() {
        // ViewModel 真正销毁时调用
        super.onCleared()
    }
}

6. 生命周期示意

// 生命周期流程
class LifecycleExample {
    fun process() {
        // 1. 创建 Activity
        val activity = Activity()
        val viewModel = ViewModelProvider(activity).get()
        
        // 2. 配置变化(比如旋转屏幕)
        activity.recreate()  // ViewModel 保持不变
        
        // 3. 真正的销毁(比如关闭页面)
        activity.finish()    // ViewModel 被清理
    }
}

关键点总结:

  1. 独立生命周期的原理
  • ViewModelStore 持有 ViewModel
  • Activity/Fragment 持有 ViewModelStore
  • 配置变化时 ViewModelStore 保持不变
  • 只有在真正销毁时才清理
  1. 状态保持
  • ViewModel 中的数据在配置变化时保持
  • SavedStateHandle 可以在进程死亡时保存状态
  1. 资源管理
  • viewModelScope 自动跟随 ViewModel 生命周期
  • onCleared 在真正销毁时清理资源

这样的设计确保了:

  • 数据在配置变化时的稳定性
  • 资源的合理管理
  • 内存的有效使用
  • 更好的用户体验

绑定Activity过程

1. ViewModelStore 机制

  • 每个 Activity 都会关联一个 ViewModelStore 对象
  • ViewModelStore 是一个简单的 HashMap 容器,用于存储 ViewModel 实例
  • key 是 ViewModel 的类名,value 是 ViewModel 实例

2. 绑定过程

// 当我们在 Activity 中调用
ViewModelProvider(this).get(MyViewModel::class.java)

实际上发生了以下步骤:

a) 获取 ViewModelStoreOwner

  • Activity 实现了 ViewModelStoreOwner 接口
  • 通过 ComponentActivity 中的 getViewModelStore() 方法获取 ViewModelStore

b) ViewModelProvider 创建过程

  • 创建 ViewModelProvider 实例时会传入 ViewModelStoreOwner
  • ViewModelProvider 会持有 ViewModelStore 的引用
  • 同时会创建默认的 Factory 用于实例化 ViewModel

c) ViewModel 实例化

  • 调用 get() 方法时,首先会从 ViewModelStore 中查找是否已存在对应的 ViewModel
  • 如果不存在,则使用 Factory 创建新的实例并存入 ViewModelStore

3. 生命周期管理

  • Activity 实现了 LifecycleOwner 接口
  • 在 Activity 销毁时(非配置变更导致的销毁),会调用 ViewModelStore.clear() 清除所有 ViewModel
  • 配置变更时(如旋转屏幕),Activity 会被销毁重建,但 ViewModel 会被保留

4. 源码核心实现

public class ViewModelProvider {
    private final ViewModelStore store;
    private final Factory factory;
    
    public <T extends ViewModel> T get(Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        ViewModel viewModel = store.get(canonicalName);
        
        if (modelClass.isInstance(viewModel)) {
            return (T) viewModel;
        }
        
        viewModel = factory.create(modelClass);
        store.put(canonicalName, viewModel);
        return (T) viewModel;
    }
}

5. 保存机制

  • Activity 在配置变更时会调用 onRetainNonConfigurationInstance()
  • 该方法会保存 ViewModelStore,使其在 Activity 重建时可以恢复
  • 新的 Activity 实例会通过 getLastNonConfigurationInstance() 获取之前保存的 ViewModelStore

6. 关键类的职责

  • ViewModelStoreOwner:定义了获取 ViewModelStore 的接口
  • ViewModelStore:存储 ViewModel 实例的容器
  • ViewModelProvider:创建和获取 ViewModel 的工厂类
  • Factory:负责实际创建 ViewModel 实例

这种设计的优点:

  1. 实现了 ViewModel 的作用域管理
  2. 保证了配置变更时数据的存活
  3. 避免了内存泄漏
  4. 使 UI 控制器(如 Activity)和数据处理解耦

需要注意的是,这种绑定机制是通过 Jetpack 架构组件实现的,而不是 Android 框架的原生功能。这也是为什么我们需要添加相应的依赖才能使用 ViewModel。

ViewModel与Activity等的绑定流程图总结

graph TD
    A[Activity/Fragment] -->|1. 调用| B["ViewModelProvider(this).get(XXViewModel.class)"]
    B -->|2. 构造| C[ViewModelProvider]
    C -->|3. 获取| D[ViewModelStore]
    D -->|4. 查找| E{是否存在<br>ViewModel实例?}
    E -->|是| F[返回已有实例]
    E -->|否| G[Factory.create]
    G -->|5. 创建| H[新ViewModel实例]
    H -->|6. 存储| I[ViewModelStore.put]
    I -->|7. 返回| J[ViewModel实例]
    F -->|8. 返回| J

    subgraph 配置变更保存机制
        K[Activity.onRetainNonConfigurationInstance] -->|保存| L[ViewModelStore]
        M[新Activity] -->|获取| N[getLastNonConfigurationInstance]
        N -->|恢复| L
    end

    subgraph 生命周期管理
        O[Activity.onDestroy] -->|非配置变更| P[ViewModelStore.clear]
        O -->|配置变更| Q[保留ViewModel]
    end

关键步骤说明:

  1. 初始化阶段

    • Activity 调用 ViewModelProvider 的构造方法
    • 传入 this(ViewModelStoreOwner)作为参数
  2. 获取 ViewModel 阶段

    • ViewModelProvider.get() 方法被调用
    • 通过 ViewModelStore 查找是否存在实例
    • 不存在则通过 Factory 创建新实例
  3. 存储阶段

    • 新创建的 ViewModel 被存入 ViewModelStore
    • ViewModelStore 使用 HashMap 保存实例
  4. 配置变更处理

    • Activity 销毁前保存 ViewModelStore
    • 新 Activity 恢复时获取之前的 ViewModelStore
  5. 生命周期管理

    • 监听 Activity 的生命周期
    • 根据销毁原因决定是否清除 ViewModel

这个流程展示了从 ViewModel 的创建到销毁的完整生命周期,以及在配置变更时的处理机制。整个过程是由 Jetpack 架构组件自动管理的,开发者只需要关注业务逻辑的实现。

补充说明:

  1. ViewModelStore 是线程安全的
  2. ViewModel 的创建是懒加载的
  3. Factory 可以被自定义以支持带参数的 ViewModel 构造
  4. 整个机制保证了 ViewModel 的单一实例性

这个流程的设计充分体现了关注点分离和单一职责原则,使得 UI 层和数据层能够更好地解耦,同时保证了数据在配置变更时的可靠性。

ViewModel的相关流程图

让我用流程图详细展示 ViewModel 的各个关键知识点:

1. ViewModel 生命周期流程

graph TD
    A[Activity/Fragment 创建] -->|创建| B[ViewModel]
    B --> C{配置变化?}
    C -->|是| D[ViewModel 保持]
    C -->|否| E[Activity/Fragment 销毁]
    E --> F[ViewModel onCleared]
    D --> C

2. ViewModel 创建过程

flowchart LR
    A[Activity] -->|1. 请求 ViewModel| B[ViewModelProvider]
    B -->|2. 查找| C[ViewModelStore]
    C -->|3.1 存在| D[返回已有 ViewModel]
    C -->|3.2 不存在| E[Factory 创建新 ViewModel]
    E --> F[存入 ViewModelStore]
    F --> D

3. ViewModel 状态管理

graph TD
    A[ViewModel 状态] --> B[普通状态]
    A --> C[SavedState]
    
    B --> D[StateFlow/LiveData]
    B --> E[普通变量]
    
    C --> F[SavedStateHandle]
    
    D -->|配置变化| G[保持]
    E -->|配置变化| G
    F -->|进程死亡| H[恢复]

让我详细解释 SavedState:

1. SavedState 是什么?
class OrderViewModel(
    // SavedStateHandle 用于保存和恢复数据
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    // 1. 保存数据
    init {
        // 保存订单ID
        savedStateHandle["order_id"] = "123"
        // 保存订单状态
        savedStateHandle["order_status"] = "PENDING"
    }
    
    // 2. 读取数据
    val orderId = savedStateHandle.get<String>("order_id")
    
    // 3. 使用 StateFlow
    val orderStatus = savedStateHandle.getStateFlow("order_status", "")
}

就像是:

  • 餐厅的记事本
  • 即使停电(进程被杀)
  • 重启后还能看到之前的记录
2. 什么时候需要用?
class ExampleViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    // ❌ 普通变量:进程死亡后数据丢失
    private var normalData = "data"
    
    // ❌ StateFlow:进程死亡后数据丢失
    private val _stateFlow = MutableStateFlow("data")
    
    // ✅ SavedStateHandle:进程死亡后可恢复
    private val savedData = savedStateHandle.getStateFlow("key", "data")
}

使用场景:

  • 用户填写的表单数据
  • 页面滚动位置
  • 关键的业务状态
3. 工作原理
// 1. 保存数据
class SaveExample(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    
    fun saveData() {
        savedStateHandle["user_input"] = "用户输入"  // 会自动保存到 Bundle
    }
}

// 2. 进程被杀死后恢复
class RestoreExample(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    
    init {
        // 自动恢复之前保存的数据
        val userInput = savedStateHandle.get<String>("user_input")
        // 继续使用恢复的数据
    }
}

工作流程:

  1. 数据保存到 SavedStateHandle
  2. 系统将数据序列化到 Bundle
  3. 进程被杀死
  4. 用户重新打开应用
  5. 系统从 Bundle 恢复数据
  6. SavedStateHandle 重建状态
4. 使用建议
class BestPracticeViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    // 1. 定义键值
    companion object {
        private const val KEY_USER_DATA = "user_data"
        private const val KEY_SCROLL_POSITION = "scroll_position"
    }
    
    // 2. 使用 StateFlow
    val userData = savedStateHandle.getStateFlow(KEY_USER_DATA, UserData())
    
    // 3. 更新数据
    fun updateUserData(newData: UserData) {
        savedStateHandle[KEY_USER_DATA] = newData
    }
    
    // 4. 保存滚动位置
    fun saveScrollPosition(position: Int) {
        savedStateHandle[KEY_SCROLL_POSITION] = position
    }
}

优点:

  • 自动保存和恢复
  • 无需手动序列化
  • 支持 StateFlow
  • 类型安全

注意事项:

  • 只能保存简单数据类型
  • 不要保存大量数据
  • 键值要唯一
  • 注意内存使用

这样可以:

  • 提升用户体验
  • 保护用户数据
  • 处理进程死亡
  • 减少数据丢失

4. ViewModel 作用域管理

flowchart TD
    A[ViewModel] -->|创建| B[viewModelScope]
    B --> C[协程]
    
    D[Activity 销毁] -->|触发| E[ViewModel.onCleared]
    E -->|取消| B
    E -->|清理| C

5. ViewModel 与 UI 交互

sequenceDiagram
    participant UI as UI Layer
    participant VM as ViewModel
    participant R as Repository
    
    UI->>VM: 1. UI Event
    VM->>R: 2. 处理数据
    R-->>VM: 3. 返回结果
    VM-->>UI: 4. 更新 UI State

6. SavedStateHandle 工作流程

graph LR
    A[ViewModel] -->|存储| B[SavedStateHandle]
    B -->|保存| C[Bundle]
    C -->|进程死亡| D[系统保存]
    D -->|恢复| E[新 Bundle]
    E -->|恢复| F[新 SavedStateHandle]
    F -->|恢复| G[新 ViewModel]

7. ViewModel 清理流程

flowchart TD
    A[Activity/Fragment 销毁] -->|检查| B{配置变化?}
    B -->|是| C[保持 ViewModel]
    B -->|否| D[ViewModelStore.clear]
    D --> E[调用所有 ViewModel.onCleared]
    E --> F[取消 viewModelScope]
    F --> G[清理资源]

8. ViewModel 数据流

flowchart LR
    A[数据源] -->|获取数据| B[Repository]
    B -->|处理数据| C[ViewModel]
    C -->|StateFlow/LiveData| D[UI State]
    D -->|Compose/XML| E[UI 展示]
    E -->|用户操作| F[UI Event]
    F -->|处理事件| C

9. ViewModel 实例共享

graph TD
    A[Activity] -->|共享| B[ViewModel]
    C[Fragment1] -->|访问| B
    D[Fragment2] -->|访问| B
    B -->|状态更新| C
    B -->|状态更新| D

10. 完整架构流程

graph TD
    A[UI Layer] -->|Events| B[ViewModel Layer]
    B -->|States| A
    B -->|Data Ops| C[Repository Layer]
    C -->|Results| B
    
    subgraph "ViewModel Scope"
        B -->|管理| D[ViewModelScope]
        B -->|状态| E[SavedStateHandle]
    end

这些流程图展示了:

  1. ViewModel 的生命周期管理
  2. 状态保存和恢复机制
  3. 协程作用域管理
  4. UI 交互流程
  5. 数据流向
  6. 资源清理过程
  7. 实例共享机制

帮助理解:

  • ViewModel 的工作原理
  • 各组件之间的关系
  • 数据流转过程
  • 生命周期管理
  • 最佳实践方式

ViewModel中的协程的使用

1. 基本概念

想象 ViewModel 是一个工厂:

  • viewModelScope 是工厂的工作时间
  • 协程是工厂里的工人
  • 当工厂关门(ViewModel 清除),所有工人都会停止工作(协程取消)

2. 基础使用示例

class UserViewModel : ViewModel() {
    private val _userState = MutableStateFlow<UiState>(UiState.Initial)
    val userState = _userState.asStateFlow()

    // 加载用户信息
    fun loadUserInfo() {
        // 启动一个工人(协程)去工作
        viewModelScope.launch {
            try {
                // 1. 更新状态为加载中
                _userState.value = UiState.Loading
                
                // 2. 执行耗时操作
                val user = userRepository.getUserInfo()
                
                // 3. 更新成功状态
                _userState.value = UiState.Success(user)
            } catch (e: Exception) {
                // 4. 更新错误状态
                _userState.value = UiState.Error(e.message)
            }
        }
    }
}

3. 不同协程作用域

graph TD
    A[协程作用域] --> B[viewModelScope]
    A --> C[lifecycleScope]
    A --> D[GlobalScope]
    
    B --> B1[ViewModel生命周期]
    C --> C1[Activity/Fragment生命周期]
    D --> D1[应用程序生命周期]

4. 常见使用场景

4.1 并行请求
class ProductViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            // 同时派出多个工人工作
            val products = async { repository.getProducts() }
            val categories = async { repository.getCategories() }
            
            // 等待所有工人完成工作
            try {
                val productList = products.await()
                val categoryList = categories.await()
                // 处理结果
            } catch (e: Exception) {
                // 处理错误
            }
        }
    }
}
4.2 顺序请求
class OrderViewModel : ViewModel() {
    fun processOrder() {
        viewModelScope.launch {
            try {
                // 工人按顺序完成工作
                val order = createOrder()        // 第一步:创建订单
                val payment = processPayment(order)  // 第二步:处理支付
                updateOrderStatus(payment)       // 第三步:更新状态
            } catch (e: Exception) {
                // 处理错误
            }
        }
    }
}

5. 错误处理

class ErrorHandlingViewModel : ViewModel() {
    fun handleErrors() {
        viewModelScope.launch {
            try {
                // 可能出错的操作
                riskyOperation()
            } catch (e: Exception) {
                when (e) {
                    is NetworkException -> handleNetworkError()
                    is DatabaseException -> handleDatabaseError()
                    else -> handleUnknownError()
                }
            } finally {
                // 清理工作
                cleanup()
            }
        }
    }
}

6. 取消操作

class SearchViewModel : ViewModel() {
    private var searchJob: Job? = null
    
    fun search(query: String) {
        // 取消之前的搜索工作
        searchJob?.cancel()
        
        // 启动新的搜索
        searchJob = viewModelScope.launch {
            try {
                delay(300) // 防抖
                val results = repository.search(query)
                // 处理结果
            } catch (e: CancellationException) {
                // 搜索被取消
            }
        }
    }
}

7. 协程作用域选择

class MyViewModel : ViewModel() {
    // 1. viewModelScope:随ViewModel生命周期
    fun operation1() {
        viewModelScope.launch {
            // 适合:数据加载、网络请求等
        }
    }
    
    // 2. 自定义作用域:特殊需求
    private val customScope = CoroutineScope(
        Dispatchers.IO + SupervisorJob()
    )
    
    fun operation2() {
        customScope.launch {
            // 适合:特殊的取消策略
        }
    }
}

8. 最佳实践

class BestPracticeViewModel : ViewModel() {
    // 1. 状态管理
    private val _state = MutableStateFlow<UiState>(UiState.Initial)
    val state = _state.asStateFlow()

    // 2. 错误处理
    private val _error = MutableSharedFlow<String>()
    val error = _error.asSharedFlow()

    fun loadData() {
        viewModelScope.launch {
            try {
                _state.value = UiState.Loading
                
                // 使用 withContext 切换线程
                val result = withContext(Dispatchers.IO) {
                    repository.getData()
                }
                
                _state.value = UiState.Success(result)
            } catch (e: Exception) {
                _state.value = UiState.Error
                _error.emit(e.message ?: "Unknown error")
            }
        }
    }
}

总结:

  1. 基本原则

    • 使用 viewModelScope 管理协程生命周期
    • 适当处理错误情况
    • 注意取消不需要的操作
  2. 常见用法

    • 网络请求
    • 数据库操作
    • 并行任务
    • 顺序任务
  3. 注意事项

    • 正确选择协程作用域
    • 合理处理异常
    • 及时取消不需要的任务
    • 使用适当的调度器
  4. 最佳实践

    • 使用 Flow 管理状态
    • 统一的错误处理
    • 合理的线程切换
    • 清晰的代码结构

记住:

  • ViewModel 是工厂(管理协程的地方)
  • viewModelScope 是工作时间(协程的生命周期)
  • 协程是工人(执行具体任务)
  • 工厂关门(ViewModel 清除)时,所有工作自动停止(协程取消)

ViewModel为何不建议直接持有Context?

让我深入解析为什么 ViewModel 不建议直接持有 Context。

🌟 核心问题

// 错误示例
class WrongViewModel(
    private val context: Context  // ❌ 不应该直接持有
) : ViewModel() {
    // 可能导致内存泄漏
}

// 正确示例
class CorrectViewModel(
    private val repository: Repository  // ✅ 依赖注入
) : ViewModel() {
    // 生命周期安全
}

💡 主要原因

// 1. 内存泄漏风险
class MemoryLeakExample : ViewModel() {
    /* 
    问题场景:
    - Activity 旋转屏幕重建
    - ViewModel 存活
    - Context 引用无法释放
    - 导致原 Activity 泄漏
    */
    private val context: Context? = null  // 危险!
    
    fun riskyOperation() {
        context?.let {
            // 使用 Context 的操作
            // 可能在 Activity 销毁后仍然持有引用
        }
    }
}

// 2. 生命周期不一致
class LifecycleConflictExample : ViewModel() {
    /* 
    生命周期差异:
    - Activity:配置变更时重建
    - ViewModel:配置变更时保持
    - Context:跟随 Activity 销毁
    */
    
    fun demonstrateLifecycleMismatch() {
        // Activity 重建
        // ViewModel 保持
        // Context 变得无效
    }
}

⚡ 正确处理方式

// 1. 使用 Application Context
class SafeViewModel(
    application: Application
) : AndroidViewModel(application) {
    // 安全地使用 Application Context
    private val appContext = application.applicationContext
    
    fun safeOperation() {
        // 使用 Application Context
        appContext.resources
    }
}

// 2. 依赖注入
class DependencyInjectionExample(
    private val repository: Repository,
    private val useCase: UseCase
) : ViewModel() {
    // 通过依赖注入获取所需依赖
    // 而不是直接持有 Context
}

// 3. 使用 Flow/LiveData
class DataStreamExample : ViewModel() {
    private val _data = MutableStateFlow<Data>(Data.Empty)
    val data: StateFlow<Data> = _data.asStateFlow()
    
    // 通过数据流传递,而不是直接操作 Context
}

🔄 最佳实践

// 1. AndroidViewModel 使用
class SafeAndroidViewModel(
    application: Application
) : AndroidViewModel(application) {
    
    // 需要 Context 的操作都在 Repository 层处理
    private val repository: Repository = Repository(
        application.applicationContext
    )
    
    fun loadData() {
        viewModelScope.launch {
            repository.getData()
        }
    }
}

// 2. Repository 模式
class Repository(
    private val context: Context
) {
    // Repository 负责处理需要 Context 的操作
    fun getResources(): Resources {
        return context.resources
    }
}

// 3. Use Case 模式
class UseCase(
    private val context: Context
) {
    // 业务逻辑封装在 Use Case 中
    fun execute() {
        // 处理需要 Context 的操作
    }
}

⚠️ 常见错误

// 1. 直接持有 Activity
class ProblematicViewModel(
    private val activity: Activity  // ❌ 严重错误
) : ViewModel()

// 2. 保存 View 引用
class ViewHoldingViewModel : ViewModel() {
    private var button: Button? = null  // ❌ 不应该持有
}

// 3. 存储 Context 相关对象
class ContextDependentViewModel : ViewModel() {
    private lateinit var layoutInflater: LayoutInflater  // ❌ 避免
}

📊 影响分析

// 内存泄漏示例
class LeakAnalysis {
    /* 
    泄漏场景:
    1. Activity 重建
       ViewModel 存活
       Context 引用未释放
       
    2. 内存占用增加
       性能下降
       可能导致 OOM
    */
    
    fun demonstrateLeak() {
        val viewModel = WrongViewModel(activity)
        // 屏幕旋转
        // Activity 重建
        // 原 Activity 无法释放
    }
}

💡 解决方案

  1. 架构层面:
- 使用依赖注入
- 采用 Repository 模式
- 实现 Clean Architecture
  1. 代码层面:
- 使用 AndroidViewModel
- 通过 Application Context
- 封装 Context 相关操作
  1. 生命周期处理:
- 遵循 MVVM 架构
- 使用 Flow/LiveData
- 正确处理配置变更

记住:

  1. ViewModel 不应直接持有 Context
  2. 使用 Application Context 或依赖注入
  3. 将 Context 相关操作封装在 Repository
  4. 注意内存泄漏风险

ViewModel 的"僵尸复活"问题

让我详细解释 ViewModel 的"僵尸复活"问题及其解决方案:

一、什么是"僵尸复活"问题?

"僵尸复活"(Zombie ViewModel)指的是:

  1. Activity 被系统销毁(比如配置更改或内存不足)
  2. ViewModel 实例仍然保留在 ViewModelStore 中
  3. Activity 重建时,原来的 ViewModel 会被重新关联到新的 Activity
  4. 如果 ViewModel 持有了原 Activity 的引用,就会导致内存泄漏或不正确的状态

二、产生原因

class MyViewModel : ViewModel() {
    // 错误示例:直接持有 Activity 引用
    private lateinit var activity: MainActivity
    
    // 错误示例:持有 Context 引用
    private lateinit var context: Context
    
    // 错误示例:持有 View 引用
    private lateinit var button: Button
}

主要原因:

  1. ViewModel 错误地持有 Activity/Context/View 等引用
  2. 配置更改时 Activity 重建,但 ViewModel 保持存活
  3. 新 Activity 实例与旧的 ViewModel 关联,导致引用错乱

三、解决方案

  1. 使用 AndroidViewModel
class MyAndroidViewModel(application: Application) : AndroidViewModel(application) {
    // 安全地使用 Application Context
    private val appContext = application.applicationContext
}
  1. 使用 LiveData 通信
class MyViewModel : ViewModel() {
    private val _events = MutableLiveData<Event<String>>()
    val events: LiveData<Event<String>> = _events
    
    fun doSomething() {
        _events.value = Event("Action completed")
    }
}
  1. 使用 Flow 通信
class MyViewModel : ViewModel() {
    private val _events = MutableSharedFlow<String>()
    val events = _events.asSharedFlow()
    
    fun doSomething() {
        viewModelScope.launch {
            _events.emit("Action completed")
        }
    }
}
  1. 使用 SavedStateHandle
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    // 安全地保存和恢复状态
    var userName: String?
        get() = savedStateHandle.get<String>("user_name")
        set(value) = savedStateHandle.set("user_name", value)
}
  1. 使用 Event 包装器
class Event<T>(private val content: T) {
    private var hasBeenHandled = false
    
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }
    
    fun peekContent(): T = content
}

四、最佳实践

  1. 避免持有 Activity 引用
// 错误方式
class WrongViewModel : ViewModel() {
    private var activity: Activity? = null
    
    fun setActivity(activity: Activity) {
        this.activity = activity
    }
}

// 正确方式
class CorrectViewModel : ViewModel() {
    private val _navigation = MutableLiveData<NavigationEvent>()
    val navigation: LiveData<NavigationEvent> = _navigation
}
  1. 使用 Event 处理一次性事件
class MyViewModel : ViewModel() {
    private val _showToast = MutableLiveData<Event<String>>()
    val showToast: LiveData<Event<String>> = _showToast
    
    fun showMessage(message: String) {
        _showToast.value = Event(message)
    }
}
  1. 使用 StateFlow 管理状态
class MyViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState())
    val uiState = _uiState.asStateFlow()
    
    fun updateState(newState: UiState) {
        _uiState.value = newState
    }
}
  1. 正确处理配置更改
class MyViewModel : ViewModel() {
    // 使用 SavedStateHandle 保存关键数据
    private val savedStateHandle: SavedStateHandle
    
    // 使用 StateFlow 管理 UI 状态
    private val _uiState = MutableStateFlow<UiState>(UiState.Initial)
    val uiState = _uiState.asStateFlow()
    
    // 使用 SharedFlow 处理一次性事件
    private val _events = MutableSharedFlow<UiEvent>()
    val events = _events.asSharedFlow()
}

五、注意事项

  1. 永远不要在 ViewModel 中持有:

    • Activity/Fragment 引用
    • View 引用
    • Context 引用(除了 Application Context)
  2. 使用正确的通信方式:

    • LiveData/StateFlow 用于状态
    • SharedFlow/Event 用于一次性事件
    • SavedStateHandle 用于状态保存
  3. 正确处理生命周期:

    • 在 onCleared() 中清理资源
    • 使用 viewModelScope 管理协程
    • 注意内存泄漏问题

通过以上方案,可以有效避免 ViewModel 的"僵尸复活"问题,保持应用状态的一致性和可靠性。

为什么Fragment中获取的ViewModel会和Activity共享?如何实现作用域隔离?

让我来解释 Fragment 和 Activity 中 ViewModel 共享的原理以及如何实现作用域隔离:

1. 为什么会共享?

// Fragment 中
class MyFragment : Fragment() {
    // 默认情况下,这里获取的 ViewModel 实际上是绑定到 Activity 的生命周期
    val viewModel: MyViewModel by viewModels()  
}

// Activity 中
class MyActivity : AppCompatActivity() {
    // 这里获取的 ViewModel 和上面的 Fragment 是同一个实例
    val viewModel: MyViewModel by viewModels()
}

原因是:

  • by viewModels() 默认使用 Activity 作为 ViewModelStoreOwner
  • ViewModelStore 是存储在 ViewModelStoreOwner 中的
  • 同一个 ViewModelStoreOwner 中的同类型 ViewModel 只会创建一个实例

2. 如何实现作用域隔离:

方法一:使用 Fragment 自己作为 ViewModelStoreOwner

class MyFragment : Fragment() {
    // 指定 this 作为 ViewModelStoreOwner,确保 ViewModel 仅在 Fragment 范围内
    private val viewModel: MyViewModel by viewModels { this }
    
    // 或者直接使用
    private val viewModel: MyViewModel by viewModels()  // Fragment 默认使用自己作为 owner
}

方法二:使用不同的 ViewModel 类

// Fragment 专用的 ViewModel
class FragmentViewModel : ViewModel()

// Activity 专用的 ViewModel
class ActivityViewModel : ViewModel()

class MyFragment : Fragment() {
    // 这个 ViewModel 不会和 Activity 共享
    private val viewModel: FragmentViewModel by viewModels()
}

class MyActivity : AppCompatActivity() {
    // 这个是不同的 ViewModel 类型
    private val viewModel: ActivityViewModel by viewModels()
}

3. 常见使用场景:

场景一:Fragment 需要自己的独立 ViewModel

class UserListFragment : Fragment() {
    // Fragment 范围的 ViewModel
    private val viewModel: UserListViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 这个 ViewModel 的生命周期跟随 Fragment
        viewModel.users.observe(viewLifecycleOwner) { users ->
            // 更新 UI
        }
    }
}

场景二:多个 Fragment 需要共享数据

class SharedViewModel : ViewModel() {
    private val _sharedData = MutableLiveData<String>()
    val sharedData: LiveData<String> = _sharedData
}

class Fragment1 : Fragment() {
    // 使用 Activity 范围的 ViewModel
    private val sharedViewModel: SharedViewModel by activityViewModels()
}

class Fragment2 : Fragment() {
    // 同样使用 Activity 范围的 ViewModel
    private val sharedViewModel: SharedViewModel by activityViewModels()
}

场景三:嵌套 Fragment 的情况

class ParentFragment : Fragment() {
    // 父 Fragment 的 ViewModel
    private val parentViewModel: ParentViewModel by viewModels()
    
    // 如果要和子 Fragment 共享,使用 Fragment 作为 ViewModelStoreOwner
    private val sharedViewModel: SharedViewModel by viewModels()
}

class ChildFragment : Fragment() {
    // 获取父 Fragment 的 ViewModel
    private val sharedViewModel: SharedViewModel by viewModels(
        ownerProducer = { requireParentFragment() }
    )
}

4. 注意事项:

1. 作用域选择建议:

// 当数据只在单个 Fragment 使用时
private val viewModel: MyViewModel by viewModels()

// 当数据需要在多个 Fragment 间共享时
private val viewModel: SharedViewModel by activityViewModels()

// 当需要在父子 Fragment 间共享时
private val viewModel: SharedViewModel by viewModels { requireParentFragment() }

2. 生命周期注意:

class MyFragment : Fragment() {
    private val viewModel: MyViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 使用 viewLifecycleOwner 而不是 this 来观察数据
        viewModel.data.observe(viewLifecycleOwner) { data ->
            // 更新 UI
        }
    }
}

3. 内存管理:

  • Fragment 范围的 ViewModel 会在 Fragment 销毁时清理
  • Activity 范围的 ViewModel 会在 Activity 销毁时清理
  • 选择合适的作用域可以避免内存泄漏

总结:

  • 默认情况下 Fragment 中的 ViewModel 是和 Activity 共享的
  • 使用 by viewModels() 可以让 ViewModel 仅在 Fragment 范围内
  • 使用不同的 ViewModel 类也可以实现隔离
  • 根据实际需求选择合适的作用域很重要

多个Activity共享ViewModel

在 Android 中让两个 Activity 共享 ViewModel 主要有两种方法:

1. 使用 ActivityViewModel

// 1. 首先定义共享的 ViewModel
class SharedViewModel : ViewModel() {
    private val _sharedData = MutableLiveData<String>()
    val sharedData: LiveData<String> = _sharedData

    fun updateData(newData: String) {
        _sharedData.value = newData
    }
}

// 2. 在两个 Activity 中使用同一个 ViewModelStore Owner
class MainActivity : AppCompatActivity() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 观察数据变化
        sharedViewModel.sharedData.observe(this) { data ->
            // 处理数据更新
        }
    }
}

class SecondActivity : AppCompatActivity() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 更新数据
        sharedViewModel.updateData("新数据")
    }
}

2. 使用自定义 ViewModelStoreOwner

// 1. 创建一个自定义的 ViewModelStoreOwner
class MyApplication : Application(), ViewModelStoreOwner {
    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

    override fun getViewModelStore(): ViewModelStore = appViewModelStore
}

// 2. 在 Activity 中使用
class MainActivity : AppCompatActivity() {
    private val sharedViewModel: SharedViewModel by viewModels {
        ViewModelProvider.AndroidViewModelFactory.getInstance((application as MyApplication))
    }
}

class SecondActivity : AppCompatActivity() {
    private val sharedViewModel: SharedViewModel by viewModels {
        ViewModelProvider.AndroidViewModelFactory.getInstance((application as MyApplication))
    }
}

3. 使用 Navigation Component(推荐)

如果使用 Navigation Component,可以这样:

// 在 nav_graph.xml 中定义导航
<navigation>
    <fragment
        android:id="@+id/firstFragment"
        android:name=".FirstFragment">
        <action
            android:id="@+id/action_first_to_second"
            app:destination="@id/secondFragment" />
    </fragment>
    
    <fragment
        android:id="@+id/secondFragment"
        android:name=".SecondFragment" />
</navigation>

// 在 Fragment 中共享 ViewModel
class FirstFragment : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
}

class SecondFragment : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
}

4. 使用 Hilt(依赖注入方式)

@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity()

@AndroidEntryPoint
class SecondActivity : AppCompatActivity()

@HiltViewModel
class SharedViewModel @Inject constructor() : ViewModel()

// 在两个 Activity 中注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var sharedViewModel: SharedViewModel
}

@AndroidEntryPoint
class SecondActivity : AppCompatActivity() {
    @Inject
    lateinit var sharedViewModel: SharedViewModel
}

最佳实践建议

  1. 使用 Navigation Component
  • 如果你的 App 使用单 Activity 多 Fragment 架构
  • 这是 Google 推荐的现代架构方式
  1. 使用 Hilt
  • 如果你的项目使用依赖注入
  • 提供更好的解耦和测试性能
  1. 使用 Application 范围
  • 如果确实需要在多个 Activity 间共享数据
  • 注意内存泄漏问题
// 示例:完整的共享 ViewModel 实现
class SharedViewModel : ViewModel() {
    // 使用 Flow 而不是 LiveData 以获得更好的协程支持
    private val _sharedState = MutableStateFlow<UiState>(UiState.Initial)
    val sharedState = _sharedState.asStateFlow()

    sealed class UiState {
        object Initial : UiState()
        data class Content(val data: String) : UiState()
        data class Error(val message: String) : UiState()
    }

    fun updateState(newData: String) {
        viewModelScope.launch {
            try {
                _sharedState.value = UiState.Content(newData)
            } catch (e: Exception) {
                _sharedState.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }

    // 清理资源
    override fun onCleared() {
        super.onCleared()
        // 清理工作
    }
}

记住:

  1. ViewModel 生命周期长于 Activity
  2. 注意内存泄漏
  3. 合理管理数据状态
  4. 考虑使用 SavedStateHandle 保存状态