深入解析 Jetpack ViewModel:从架构设计到源码实现

85 阅读6分钟

一、ViewModel 的核心设计理念

ViewModel 作为 Jetpack 架构组件的核心成员,其设计目标聚焦于解决两大开发痛点:

  1. 配置变更时的数据丢失(如屏幕旋转导致 Activity 重建)

  2. 组件间的数据共享(如 Fragment 与 Activity、Fragment 之间通信)

核心特性

  • 生命周期感知:与宿主组件(Activity/Fragment)的生命周期绑定,但不受配置变更影响

  • 自动资源释放:宿主组件进入DESTROYED状态时自动清理

  • 轻量级存储:专注于存储与 UI 相关的业务数据,不持有 View 引用

典型应用场景

java

// 正确示例:ViewModel存储列表数据,配置变更时不丢失
class NewsViewModel extends ViewModel {
    private final MutableLiveData<List<Article>> articles = new MutableLiveData<>();
    
    void loadNews() {
        // 网络请求或本地加载数据
        articles.setValue(repository.fetchArticles());
    }
    
    LiveData<List<Article>> getArticles() {
        return articles;
    }
}

二、ViewModel 的生命周期与宿主绑定机制

2.1 生命周期协同原理

ViewModel 的生命周期与宿主组件的生命周期存在以下关系:

  • 创建时机:首次通过ViewModelProvider获取时创建

  • 存活周期:从创建到宿主组件完成onDestroy()(非配置变更导致的销毁)

  • 销毁时机:宿主组件的onCleared()方法调用时释放资源

java

// ViewModel与Activity的生命周期对比
Activity: onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy()
ViewModel: created       ->        active         ->        inactive        -> onCleared()
2.2 配置变更时的实例保留

ViewModel 之所以能在配置变更时保留实例,核心在于ViewModelStoreFragmentActivity的协同:

  1. FragmentActivity在重建时会从ViewModelStore中获取已存在的 ViewModel

  2. ViewModelStore通过HashMapViewModel的类名作为键存储实例

  3. 新 Activity 通过NonConfigurationInstances获取前一个 Activity 的ViewModelStore

java

// ViewModelProvider创建逻辑
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

// ViewModelStore核心实现
public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    public <T extends ViewModel> T get(String key) {
        ViewModel viewModel = mMap.get(key);
        if (viewModel == null) {
            throw new ViewModelNotFoundException("No ViewModel found with key " + key);
        }
        return (T) viewModel;
    }
    
    public void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
}

三、源码深度解析:从创建到销毁的完整流程

3.1 ViewModelProvider 的工厂模式

ViewModel 的创建通过工厂模式实现,支持自定义实例化逻辑:

  • NewInstanceFactory:默认工厂,通过反射创建 ViewModel 实例

  • AndroidViewModelFactory:支持注入 Application 上下文,用于 AndroidViewModel

  • 自定义 Factory:支持构造函数参数注入

java

// 自定义ViewModelFactory示例
public class UserViewModelFactory extends ViewModelProvider.Factory {
    private final UserRepository repository;
    private final String userId;
    
    public UserViewModelFactory(UserRepository repository, String userId) {
        this.repository = repository;
        this.userId = userId;
    }
    
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (modelClass.isAssignableFrom(UserViewModel.class)) {
            // 带参数的ViewModel构造
            return (T) new UserViewModel(repository, userId);
        }
        throw new IllegalArgumentException("Unknown ViewModel class");
    }
}
3.2 ViewModel 的销毁机制

ViewModel 的销毁通过onCleared()方法实现,该方法在以下场景调用:

  1. 宿主组件完成onDestroy()且非配置变更

  2. ViewModelStore被清除时(如 Activity 调用finish()

java

// ViewModel核心生命周期方法
public class ViewModel {
    @CallSuper
    protected void onCleared() {
        // 子类重写此方法释放资源
    }
    
    // 防止内存泄漏的关键:不持有Activity引用
    private WeakReference<Activity> activityRef; // 反模式!应避免
}
3.3 ViewModel 与 Lifecycle 的协同

ViewModel 通过LifecycleObserver监听宿主组件的生命周期状态,确保资源合理释放:

java

// ViewModelProvider在Activity中的初始化
public class ComponentActivity extends androidx.core.app.ComponentActivity implements 
        HasDefaultViewModelProviderFactory,
        ViewModelStoreOwner,
        HasViewModelStore {
    
    private ViewModelStore mViewModelStore;
    
    @Override
    public ViewModelStore getViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc = 
                (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 从NonConfigurationInstances获取已存在的ViewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
}

四、高级应用场景与最佳实践

4.1 多 ViewModel 协同与数据共享

java

// 主ViewModel管理多个子ViewModel
class MainViewModel extends ViewModel {
    private final UserViewModel userViewModel;
    private final NewsViewModel newsViewModel;
    
    public MainViewModel(
            ViewModelProvider.Factory factory,
            UserRepository userRepo,
            NewsRepository newsRepo) {
        // 通过工厂创建子ViewModel
        userViewModel = new ViewModelProvider(this, 
            new UserViewModelFactory(userRepo)).get(UserViewModel.class);
        newsViewModel = new ViewModelProvider(this, 
            new NewsViewModelFactory(newsRepo)).get(NewsViewModel.class);
    }
    
    // 合并多个LiveData数据
    public LiveData<CombinedData> getCombinedData() {
        return Transformations.switchMap(userViewModel.getUser(), user -> {
            return Transformations.map(newsViewModel.getLatestNews(), news -> {
                return new CombinedData(user, news);
            });
        });
    }
}
4.2 ViewModel 与协程的结合

java

// 使用协程构建ViewModel
class PhotoViewModel extends ViewModel {
    private final PhotoRepository repository;
    private final CoroutineScope scope = new CoroutineScope(
        Dispatchers.IO + SupervisorJob() + viewModelJob
    );
    
    private val _photos = MutableStateFlow<List<Photo>>(emptyList())
    val photos: StateFlow<List<Photo>> get() = _photos
    
    init {
        loadPhotos()
    }
    
    private fun loadPhotos() {
        scope.launch {
            try {
                val result = repository.getPhotos()
                _photos.emit(result)
            } catch (e: Exception) {
                // 错误处理
            }
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        scope.cancel() // 销毁时取消协程
    }
}
4.3 跨 Fragment 数据共享

java

// Activity中创建共享ViewModel
class SharedViewModel extends ViewModel {
    private final MutableLiveData<String> sharedData = new MutableLiveData<>();
    
    void setData(String data) {
        sharedData.setValue(data);
    }
    
    LiveData<String> getData() {
        return sharedData;
    }
}

// Fragment1中设置数据
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.setData("Hello from Fragment1");

// Fragment2中观察数据
model.getData().observe(getViewLifecycleOwner(), data -> {
    textView.setText(data);
});

五、性能优化与陷阱规避

5.1 避免内存泄漏的最佳实践

错误示例(反模式):

java

class BadViewModel extends ViewModel {
    private Activity activity; // 持有Activity引用,导致内存泄漏
    
    public BadViewModel(Activity activity) {
        this.activity = activity;
    }
}

正确做法

java

class GoodViewModel extends ViewModel {
    // 使用Application上下文(生命周期与应用一致)
    private final Application application;
    
    public GoodViewModel(Application application) {
        this.application = application;
    }
    
    // 通过弱引用持有非UI对象
    private WeakReference<DatabaseHelper> dbRef;
}
5.2 异步任务的正确处理

java

// 错误示例:未处理ViewModel销毁时的任务
class ErrorViewModel extends ViewModel {
    private Disposable disposable;
    
    void loadData() {
        disposable = repository.getData()
            .subscribe(data -> { /* 更新UI */ });
    }
    
    // 正确做法:在onCleared中取消任务
    @Override
    protected void onCleared() {
        super.onCleared();
        if (disposable != null && !disposable.isDisposed()) {
            disposable.dispose();
        }
    }
}
5.3 配置变更时的任务处理

java

// 使用SavedStateHandle保存临时状态
class ConfigChangeViewModel extends ViewModel {
    private final SavedStateHandle savedStateHandle;
    
    ConfigChangeViewModel(SavedStateHandle savedStateHandle) {
        this.savedStateHandle = savedStateHandle;
    }
    
    void loadData() {
        // 检查是否已有缓存数据
        if (savedStateHandle.contains("data")) {
            return;
        }
        
        // 发起网络请求
        repository.getData().subscribe(data -> {
            savedStateHandle.set("data", data); // 保存数据到SavedState
        });
    }
}

六、ViewModel 与其他架构组件的协同

6.1 ViewModel + LiveData + Room 的经典组合

java

// Room数据库操作返回LiveData
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    LiveData<List<User>> getUsers();
    
    @Insert
    void insertUser(User user);
}

// ViewModel封装数据操作
class UserViewModel extends ViewModel {
    private final UserDao userDao;
    
    UserViewModel(UserDao userDao) {
        this.userDao = userDao;
    }
    
    LiveData<List<User>> getUsers() {
        return userDao.getUsers();
    }
    
    void addUser(User user) {
        // ViewModel负责业务逻辑,不直接操作UI
        new Thread(() -> userDao.insertUser(user)).start();
    }
}
6.2 ViewModel 与 WorkManager 的协作

java

// ViewModel启动后台任务
class DownloadViewModel extends ViewModel {
    void startDownload(String url) {
        WorkRequest request = new OneTimeWorkRequest.Builder(DownloadWorker.class)
            .setInputData(new Data.Builder()
                .putString("url", url)
                .build())
            .build();
        
        WorkManager.getInstance().enqueue(request);
    }
    
    LiveData<DownloadStatus> getDownloadStatus() {
        // 通过WorkManager获取任务状态
        return WorkManager.getInstance().getWorkInfoByIdLiveData(request.getId());
    }
}

七、ViewModel 的局限性与扩展方案

7.1 局限性
  • 不适合处理长时间运行的后台任务(如 Service)
  • 不支持跨进程数据共享(需配合 ContentProvider)
  • 构造函数依赖注入需手动处理(推荐使用 Hilt 等依赖注入框架)
7.2 与其他框架的融合

java

// 使用Hilt实现ViewModel依赖注入
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository,
    private val localDataSource: LocalDataSource
) : ViewModel() {
    
    fun getUserData(): LiveData<User> {
        return userRepository.getUserData(localDataSource.getCachedId());
    }
}

八、总结:ViewModel 在现代 Android 架构中的定位

ViewModel 通过生命周期感知配置变更免疫两大特性,成为 MVVM 架构中连接 Model 与 View 的桥梁。其核心设计遵循以下原则:

  1. 关注点分离:专注于业务逻辑,不涉及 UI 操作

  2. 声明式编程:通过 LiveData 等响应式组件暴露数据

  3. 生命周期安全:自动管理资源释放,杜绝内存泄漏

在实际开发中,合理使用 ViewModel 能显著提升代码的可维护性和可测试性,尤其适合复杂界面和需要处理配置变更的场景。结合协程、Hilt 等现代技术,ViewModel 将在 Android 架构演进中持续发挥核心作用。