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

ViewModel 原理

# ViewModel 原理

在现代Android应用开发中,ViewModel是架构组件库的一个关键部分,它在提高应用的稳定性和性能方面发挥着重要作用。在这篇文章中,我们将深入探讨ViewModel的工作原理和最佳实践。

ViewModel简介

ViewModel是Android Jetpack架构组件的一部分,它的主要目的是管理和存储与UI相关的数据。ViewModel的设计初衷是使数据能够在配置更改(如屏幕旋转)后继续存在。

工作原理

数据持久性

  • 当设备配置更改导致Activity重建时,ViewModel可以保留数据。它是通过系统保存的实例状态来实现的,从而使数据在Activity重新创建时依然可用。

生命周期意识

  • ViewModel与Activity或Fragment的生命周期紧密相连。它在Activity或Fragment的整个生命周期内都是活跃的,并在它们被完全销毁时清理。

使用ViewModel的优势

  • 数据管理:ViewModel使得管理UI相关的数据更加简单和高效。
  • 减少内存泄漏:由于ViewModel与视图的生命周期是分离的,因此减少了因为持有Activity或Fragment引用而导致的内存泄漏的风险。
  • 更好的数据持久化:ViewModel可以在配置更改时保留重要数据,避免了不必要的数据库或网络请求。

实现ViewModel

创建ViewModel类

  • ViewModel的创建需要继承ViewModel类。
  • 可以在ViewModel内部实现数据的加载和处理逻辑。

与Activity或Fragment关联

  • ViewModel通过ViewModelProvider与Activity或Fragment绑定。
  • 这样可以确保ViewModel的生命周期与Activity或Fragment的生命周期同步。

处理配置更改

  • 在配置更改(如屏幕旋转)时,ViewModel帮助保存重要的UI数据。
  • 当Activity或Fragment重新创建时,可以从ViewModel中恢复数据。

最佳实践

  • 避免在ViewModel中引用视图:ViewModel不应持有对Activity、Fragment或View的引用。
  • 使用LiveData:LiveData可以用来观察数据的变化,并在数据变化时更新UI。
  • 分离关注点:ViewModel应该专注于数据处理,而UI逻辑应该留在Activity或Fragment中。

ViewModel的创建方式

1. 使用ViewModelProvider

这是创建ViewModel的最常见方式。ViewModelProvider会与Activity或Fragment的生命周期关联,确保在配置更改时ViewModel不会被重新创建。

import androidx.lifecycle.ViewModelProvider
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundleclass MainActivity : AppCompatActivity() {private lateinit var viewModel: MyViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)viewModel = ViewModelProvider(this).get(MyViewModel::class.java)}
}

在这个例子中,MyViewModel 是通过ViewModelProvider创建的,并与MainActivity的生命周期关联。

2. 使用ViewModel的工厂方法

当需要向ViewModel传递参数时,可以使用ViewModel的工厂方法来创建ViewModel。

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundleclass MyViewModel(val myParam: String) : ViewModel() {// ViewModel的逻辑
}class MyViewModelFactory(private val myParam: String) : ViewModelProvider.Factory {override fun <T : ViewModel?> create(modelClass: Class<T>): T {if (modelClass.isAssignableFrom(MyViewModel::class.java)) {return MyViewModel(myParam) as T}throw IllegalArgumentException("Unknown ViewModel class")}
}class MainActivity : AppCompatActivity() {private lateinit var viewModel: MyViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val factory = MyViewModelFactory("Hello")viewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)}
}

在这个例子中,MyViewModel 需要一个字符串参数。因此,创建了一个MyViewModelFactory
来传递这个参数,并使用这个工厂来创建MyViewModel的实例。


ViewModel 相关面试题及回答

面试题1: ViewModel是什么,它是如何工作的?

  • 回答:
    ViewModel是一个架构组件,它的主要职责是管理界面控制器(如Activity和Fragment)的数据。它帮助保存数据,以便在配置更改(如屏幕旋转)时数据不会丢失。ViewModel的实例与特定的Activity或Fragment的生命周期绑定,但比单个Activity或Fragment的实例生命周期更长,因此能够在界面控制器重建时保持数据状态。

面试题2: 为什么在ViewModel中持有Activity的引用是一个不好的实践?

  • 回答:
    在ViewModel中持有Activity的引用会导致内存泄露,因为ViewModel的生命周期比它所关联的Activity长。如果ViewModel持有Activity的引用,即使Activity需要被销毁以释放资源,由于ViewModel的存在,它无法被垃圾回收器回收,从而导致内存泄漏。

面试题3: LiveData是什么,它如何与ViewModel配合使用?

  • 回答:
    LiveData是一个可观察的数据存储器类,特别是用于保存可观察数据,并且能在数据改变时通知视图。它与ViewModel配合使用,因为LiveData遵循观察者模式,这使得当数据改变时,UI可以立即更新。LiveData也是生命周期感知的,这意味着它只会在Activity或Fragment处于活跃状态时更新UI,从而避免内存泄漏。

面试题4: 如何在ViewModel中处理耗时任务?

  • 回答:
    在ViewModel中处理耗时任务(如网络请求)时,应该使用后台线程来避免阻塞UI线程。可以利用协程或RxJava等异步处理框架来实现。这些任务应该与ViewModel的生命周期关联,以确保在ViewModel被销毁时,相关的异步任务也会相应地被取消或清理,以避免内存泄露。

面试题5: 解释ViewModel的onCleared()方法。

  • 回答: onCleared()
    方法在ViewModel即将被销毁时调用,这通常发生在与其关联的Activity或Fragment被永久销毁时(不是由于配置更改)。这个方法是清理资源的理想位置,比如取消所有进行中的异步任务、移除监听器或者释放对外部资源的引用。

相关文章:

  • (3)(3.2) MAVLink2数据包签名(安全)
  • redis06 redis事务
  • web组态(BY组态)接入流程
  • CAN总线位时序的介绍
  • OpenMMlab AI实战营第四期培训
  • MySQL——性能调优
  • [蓝桥杯 2020 省 B1] 整除序列
  • 如何在十几秒内高效实现几十万条数据的快速插入
  • TestNG @Test注释属性- threadPoolSize属性
  • 自适应哈希索引
  • python网络爬虫教程笔记(1)
  • 【组合递归】【StringBuilder】Leetcode 17. 电话号码的字母组合
  • Android开发技术总结,附项目源码
  • 【Golang】介绍
  • 微服务中的Feign:优雅实现远程调用的秘密武器(一)
  • [nginx文档翻译系列] 控制nginx
  • Android交互
  • Bootstrap JS插件Alert源码分析
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • Docker: 容器互访的三种方式
  • ES6核心特性
  • Golang-长连接-状态推送
  • HTML-表单
  • Java比较器对数组,集合排序
  • Phpstorm怎样批量删除空行?
  • Vue.js 移动端适配之 vw 解决方案
  • 使用putty远程连接linux
  • 王永庆:技术创新改变教育未来
  • 协程
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #QT(智能家居界面-界面切换)
  • $.ajax()
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (正则)提取页面里的img标签
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • .NET 反射的使用
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • .NET框架设计—常被忽视的C#设计技巧
  • /proc/stat文件详解(翻译)
  • /var/log/cvslog 太大
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • @拔赤:Web前端开发十日谈
  • []常用AT命令解释()
  • [2016.7 day.5] T2
  • [23] GaussianAvatars: Photorealistic Head Avatars with Rigged 3D Gaussians
  • [android]-如何在向服务器发送request时附加已保存的cookie数据
  • [Angular 基础] - 数据绑定(databinding)
  • [C#基础]说说lock到底锁谁?
  • [C++从入门到精通] 14.虚函数、纯虚函数和虚析构(virtual)
  • [Contiki系列论文之2]WSN的自适应通信架构
  • [exgcd] Jzoj P1158 荒岛野人
  • [HCIE] IPSec-VPN (手工模式)