当前位置: 首页 > article >正文

Android ViewModel学习总结(源码级理解)

引言:为什么开发者需要ViewModel?

在Android开发中,屏幕旋转导致页面重建是开发者最头疼的问题之一。 

传统方案通过onSaveInstanceState保存数据,但存在序列化性能瓶颈和数据量限制(Bundle最大1MB)。 

而ViewModel的诞生,通过“生命周期穿透”能力,彻底解决了配置变更导致的数据丢失问题。 

但它的实现原理究竟是什么?本文将揭开其底层三大黑科技。

一、HolderFragment:寄生在Activity中的“数据保险箱”

原理剖析  

当调用ViewModelProviders.of(activity)时,系统会动态注入一个无UI的HolderFragment。这个Fragment的setRetainInstance(true)属性,使其在Activity重建时仍存活于内存中,其内部通过ViewModelStore对象缓存所有ViewModel实例。  

 

源码级实现(以AndroidX 2.5.1为例):  

// HolderFragment关键代码
publicclassHolderFragmentextendsFragment {
    privateViewModelStoremViewModelStore=newViewModelStore();
    
    @Override
    publicvoidonCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); // 关键黑科技!
    }
    
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }
}

此设计让ViewModelStore与Activity生命周期解耦,实现跨配置变更的数据持久化。 

 扩展:

ViewModel生命周期穿透(破解内存泄漏困局)

高频面试题:

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

技术解析:  

  1. 1. 源码级透视:

// 关键源码路径:androidx.fragment.app.FragmentViewModelLazy.kt
HolderFragment().apply {
    fragmentManager.beginTransaction().add(this, HOLDER_TAG).commitNow()
}
  • ViewModelProvider通过HolderFragment持有ViewModel实例
  • FragmentManager通过FragmentTransaction将HolderFragment绑定到宿主的生命周期
  • 2. 避坑指南:

  • 错误用法:在onCleared()中调用context引发内存泄漏
  • 正确方案:通过AndroidViewModel+ApplicationContext注入

ViewModel的"僵尸复活"机制(字节跳动压轴题)

场景还原:某电商App在屏幕旋转后购物车数据丢失,候选人无法解释ViewModel为何能存活

技术拆解

  1. 底层原理:
    • ViewModel通过HolderFragment实现生命周期隔离(源码见ViewModelStoreOwner

    • 数据存储依赖onRetainNonConfigurationInstance()方法实现跨配置保存

  2. 高频误区:
    • “ViewModel是单例模式”(错误率78%)

    • “ViewModel可以直接持有Context”(会导致内存泄漏)

  3. 解决方案:
// 正确获取ViewModel实例  
val viewModel: MyViewModel by viewModels(  
    factoryProducer = { CustomFactory() }  
)  

避坑指南
FragmentManager.findFragmentByTag("androidx.lifecycle.ViewModelProvider.Default")可查看HolderFragment状态

二、ViewModelStoreOwner:多组件共享的“数据枢纽”

场景痛点  

传统开发中,Activity与Fragment间的数据传递需要通过Bundle或接口回调,而ViewModel通过ViewModelStoreOwner接口,允许不同组件共享同一ViewModel实例。  

 

实现技巧  

  • 通过扩展ViewModelProvider的owner参数,支持Activity/Fragment/Navigation图等不同作用域
  • 在Fragment中调用ViewModelProviders.of(parentFragment),可获取父容器的ViewModel

 

案例演示:  

// 在Fragment中获取Activity级别的ViewModel
val sharedModel: SharedViewModel by viewModels(requireActivity())

这种设计使得跨组件通信的代码量减少50%以上。 

 


 

三、SavedStateHandle:进程级重建的“最后防线”

突破性能力  

ViewModel默认只能抵御配置变更,但进程被系统杀死时数据仍会丢失。通过集成SavedStateHandle,可将数据写入系统管理的Bundle,实现跨进程销毁的数据恢复。  

技术实现:  

class MyViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    val liveData: MutableLiveData<String> = savedStateHandle.getLiveData("key")
    
    init {
        savedStateHandle.setSavedStateProvider("key") { 
            Bundle().apply { putString("key", liveData.value) }
        }
    }
}

底层通过AbstractSavedStateViewModelFactory,在ViewModel初始化时自动注入SavedStateHandle对象。 

 


 

高频面试题深度解析

Q1:ViewModel与onSaveInstanceState有何本质区别?

答:  

  • 作用范围:ViewModel适用于大数据对象(如图片列表),而Bundle适合存储轻量级状态(如页面滚动位置)
  • 生命周期:ViewModel存活到组件完全销毁(如Activity调用finish()),而Bundle仅在临时重建时有效
  • 性能对比:ViewModel通过内存缓存避免序列化开销,性能提升约30倍

 

Q2:如何实现自定义生命周期的ViewModel?

进阶方案:  

  1. 1. 继承ViewModel并重写onCleared()

  2. 2. 通过ViewModelProvider.Factory注入自定义作用域

  3. 3. 使用LifecycleObserver监听特定生命周期事件

public class CustomViewModel extends ViewModel {
    @Override
    protected void onCleared() {
        // 释放资源
    }
}

总结:ViewModel的三大应用场景

  1. 1. 跨屏幕旋转:HolderFragment + ViewModelStore机制

  2. 2. 跨组件通信:ViewModelStoreOwner的多级作用域控制

  3. 3. 跨进程恢复:SavedStateHandle与Bundle的深度集成

感谢观看!!!


http://www.kler.cn/a/614915.html

相关文章:

  • python 如何打包成exe文件
  • 可拖动对象编辑器使用指南
  • 【Linux】了解基础指令(超详细)
  • Python3基础库入门(个人学习用)
  • Epoll 的本质与原理:高性能网络编程的基石
  • 调用 DeepSeek制作简单的电子宠物
  • 区块链技术在投票系统中的应用:安全、透明与去中心化
  • Linux CentOS 7 搭建我的世界服务器详细教程 (丐版 使用虚拟机搭建)
  • 横扫SQL面试——连续性登录问题
  • 【前端】使用 HTML、CSS 和 JavaScript 创建一个数字时钟和搜索功能的网页
  • AIDD-人工智能药物设计-利用自动化机器学习(AutoML)方法促进计算机模拟的ADMET特性预测
  • 破界·共生:生成式人工智能(GAI)认证重构普通人的AI进化图谱
  • 【KEIL5.3.7以上版本ARM compiler5 version】
  • 【大模型基础_毛玉仁】5.3 附加参数法:T-Patcher
  • OkHttps工具类的简单使用
  • 测试BioMaster: AI生信分析的demo测试
  • 【HarmonyOS 5】初学者如何高效的学习鸿蒙?
  • Apache Tomcat 深度解析:企业级Java Web容器的架构与实践
  • 深入了解ChatGPT之类的大语言模型笔记
  • 使用爬虫按图搜索1688商品(拍立淘)