新闻详情

新闻详情

首页 / 资讯中心 / 详情

【Android】ViewModel

发布时间:2026/9/14 20:37:38来源:尧图网络
【Android】ViewModel
一、为什么要引入ViewModel背景Activity 存在配置变更销毁重建屏幕旋转、系统字体缩放、语言切换同时 Activity 生命周期受系统控制。也就是普通的活动有以下局限屏幕旋转Activity 销毁重建内存中的临时数据全部丢失比如聊天页输入的文字、列表加载好的数据一转屏幕就没了。原生方案onSaveInstanceState保存数据但是它只适合存少量简单数据不适合大对象、列表数据。Activity 职责过重Activity 既要管 UI 绘制、点击事件又要写业务逻辑、网络请求、数据处理。MVC 模式下 Activity 又当 View 又当 Controller代码臃肿。异步任务容易内存泄漏在 Activity 里开子线程 / 网络请求线程还没执行完Activity 已经销毁。回调持有 Activity 引用 →内存泄漏、空指针崩溃。 比如以前的 AsyncTask 就大量踩这个坑。二、ViewModel 解决了什么生命周期独立于 Activity配置变更不会销毁ViewModel 对象的生命周期从 Activity 创建开始直到 Activity 真正完全销毁finish才销毁。 屏幕旋转重建 ActivityViewModel不会重建实例保留内存里的数据完好保留。分离业务逻辑和 UI为 MVVM 铺路把业务逻辑、数据处理、网络调用放到 ViewModel。 Activity/Fragment 只负责 UI设置数据、处理点击不写复杂业务。实现 UI 与业务解耦。避免内存泄漏ViewModel绝对不要持有 Activity/Fragment、View 的引用。 业务逻辑放在 ViewModel异步回调结束直接操作内部 LiveData不持有页面页面销毁 ViewModel 跟着销毁规避泄漏。Fragment 之间共享数据同一个 Activity 下多个 Fragment可以共用同一个 ViewModel 实例实现 Fragment 之间通信不需要接口回调、Bundle 传值。三、ViewModel的生命周期Activity创建 → ViewModel初始化 ↓ 屏幕旋转Activity销毁重建 ↓ 旧Activity销毁 → ViewModel被保留不销毁→ 新Activity获取同一个ViewModel实例 ↓ Activity真正销毁finish()或系统回收→ ViewModel被清理调用onCleared()ViewModel的作用域绑定到ViewModelStoreOwner如Activity/Fragment的生命周期它会一直保留在内存中直到其ViewModelStoreOwner永久消失。核心原理Activity在配置变更如旋转被销毁时会先调用retainNonConfigurationInstances()获取旧Activity中的数据其中包含ViewModel实例。这个数据会临时储存等新的Activity创建时再传递过去从而实现ViewModel的恢复。四、项目理解计数器界面我们用最简单的计数器场景把前面的理论落地页面有一个文本显示数字一个按钮点击一次数字1。我们先来看不用ViewModel的痛点再看引入ViewModel后的解决效果。1. 传统写法不用ViewModel的问题先写Activity代码很简单public class CounterActivity extends AppCompatActivity { // 计数器数据直接定义在Activity里 private int count 0; private TextView tvCount; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_counter); tvCount findViewById(R.id.tv_count); Button btnAdd findViewById(R.id.btn_add); btnAdd.setOnClickListener(v - { count; tvCount.setText(String.valueOf(count)); }); } }现在我们点击一次按钮上面的数字就加一了现在看似完成了任务但此时我们旋转屏幕、切换系统语言或者调整字体大小就会导致Activity销毁并重建onCreate会重新执行因此count就会被重置为0数字归零。这就是第一个痛点配置变更导致内存临时数据全部丢失。原生的补救方案是用onSaveInstanceStatepublic class MainActivity extends AppCompatActivity { private int count 0; private TextView tvCount; Override public void onSaveInstanceState(NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(count, count); } Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) - { Insets systemBars insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); tvCount findViewById(R.id.tv_count); Button btnAdd findViewById(R.id.btn_add); if(savedInstanceState ! null) { count savedInstanceState.getInt(count); tvCount.setText(String.valueOf(count)); } btnAdd.setOnClickListener(v - { count; tvCount.setText(String.valueOf(count)); }); } }这次我们旋转屏幕后计数就不会消失了。这种方法的固有局限只能存少量、可序列化的简单数据int、String、轻量对象无法保存大列表、Bitmap、正在执行的网络请求每个页面都要手写保存和恢复逻辑繁琐且易出错2. 引入ViewModel后的标准实现2.1 编写ViewModel类新建CounterViewModel并继承ViewModel把数据和业务逻辑从Activity迁移到这里public class CounterViewModel extends ViewModel { //计数器数据存放在ViewModel中 public int count 0; //加法业务逻辑 public void add() { count; } //ViewModel销毁时回调可用于释放资源 Override protected void onCleared() { super.onCleared(); } }2.2 Activity中获取并使用ViewModel注意绝对不要直接new CounterViewModel()必须通过ViewModelProvider获取实例这是保证实例复用的关键。public class MainActivity extends AppCompatActivity { private TextView tvCount; private CounterViewModel viewModel; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) - { Insets systemBars insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); tvCount findViewById(R.id.tv_count); Button btnAdd findViewById(R.id.btn_add); //通过ViewModelProvider获取实例绑定当前Activity viewModel new ViewModelProvider(this).get(CounterViewModel.class); //初始化显示 tvCount.setText(String.valueOf(viewModel.count)); //点击事件只做UI触发业务逻辑交给ViewModel btnAdd.setOnClickListener(v - { viewModel.add(); tvCount.setText(String.valueOf(viewModel.count)); }); } }这时候我们发现他也可以实现旋转屏幕数据不丢失的效果。背后的逻辑旋转时旧Activity执行销毁但ViewModel实例不会被回收新Activity重建后再次执行new ViewModelProvider...拿到的是同一个旧实例实例内部的count值完整保留因此数据不会丢失为什么必须用 ViewModelProvider 获取实例原因只有 ViewModelProvider 能帮我们维护 ViewModel 生命周期在配置变更屏幕旋转时复用同一个实例页面真正销毁时自动清理。如果我们直接new MyViewModel()每次 Activity 重建都会新建一个全新 ViewModel 对象之前存的数据也就全部丢失了那ViewModelProvider 做了什么呢它的内部有一个ViewModelStore存储容器以 Activity/Fragment 作为 key 保存 ViewModel 实例配置变更时Activity 销毁重建但是 ViewModelStore保留取出原来的 ViewModel数据保留。重点ViewModel 不是由 Activity 直接持有而是存放在 ViewModelStore。ViewModelProvider 就是用来从这个 Store 里拿实例的工具。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

在半导体制造的后道工序中,半导体测试机与分选机或探针台的自动化协同,是整个封测厂(OSAT)上位机系统(EAP / Tester Controller / ATE-MES)的核心 2026/9/14 21:19:42

在半导体制造的后道工序中,半导体测试机与分选机或探针台的自动化协同,是整个封测厂(OSAT)上位机系统(EAP / Tester Controller / ATE-MES)的核心

在半导体制造的后道工序(Back-End / ATE Test)中,半导体测试机(Tester,如 Advantest、Teradyne)与分选机(Handler,如 Delta, Cohu)或探针台(Prober,如 TEL, Accretech)的自动化协同,是整个封测厂(OSAT)上位机系统(EAP / Tester Controller / ATE-MES)的核心。…

阅读更多 →
直流配电网最优潮流建模与YALMIP实现 2026/9/14 21:19:42

直流配电网最优潮流建模与YALMIP实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
贪心算法与单调栈:数字删除问题的最优解 2026/9/14 21:19:42

贪心算法与单调栈:数字删除问题的最优解

1. 问题背景与核心需求这个算法问题看似简单,却蕴含着典型的贪心算法思想。给定一个12位整数,我们需要删除其中8个数字(保留4个),使得剩下的数字按原始顺序排列时,形成的4位数是所有可能组合中最小的那个。…

阅读更多 →
GitLab CI/CD配置文件.gitlab-ci.yml详解与实战 2026/9/14 21:19:42

GitLab CI/CD配置文件.gitlab-ci.yml详解与实战

1. .gitlab-ci.yml文件的核心作用解析.gitlab-ci.yml是GitLab CI/CD流水线的核心配置文件,它定义了自动化构建、测试和部署的完整流程。这个YAML格式的文件需要放置在项目根目录下,GitLab Runner会根据其中的指令自动执行预设任务。关键特性:…

阅读更多 →
Kubernetes镜像自动化构建与部署的Shell脚本实践 2026/9/14 21:19:42

Kubernetes镜像自动化构建与部署的Shell脚本实践

1. 项目背景与核心需求在容器化部署成为主流的今天,Kubernetes(k8s)集群中的镜像管理一直是运维工作的重点环节。每次代码更新后,开发团队需要手动构建Docker镜像、打标签、推送到私有仓库,再通知运维人员更新部署——…

阅读更多 →
免费学术查重工具评测与使用指南 2026/9/14 21:16:41

免费学术查重工具评测与使用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞