MVVM架构以及Android Data Binding的学习

本文探讨了MVVM架构的优势及其实现方式,包括低耦合、可重用性、独立开发与可测试性等特点。介绍了DataBinding如何简化Android开发中的代码编写过程,通过声明式布局减少findViewById等操作,提高开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

偶然的机会接触到MVVM框架的项目,于是准备花时间先研究一下MVVM框架。同时用到MVVM架构时会用到Data Binding,顺带一起学习。

首先看看mvvm的优点

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点
1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。
4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

MVVM最早是微软提出的

这里写图片描述

从图中可以看出,view层和viewmodel层是相互绑定的关系,这意味着当你更新viewmodel层的数据的时候,view层会相应的变动ui。

这里先将Data Binding的学习也拿过来^_^

谷歌 I/O大会上介绍了一个非常厉害的新框架DataBinding, 数据绑定框架给我们带来了很大的方便,以前我们可能需要在每个Activity里写很多的findViewById,不仅麻烦,还增加了代码的耦合性,如果我们使用DataBinding,就可以抛弃那么多的findViewById,省时省力。DataBinding 是基于MVVM思想实现数据和UI绑定的的框架,有了 Data Binding,在Android中也可以很方便的实现MVVM。
在引入DataBinding之前,我们需要敲很多很鸡肋的代码,如 findViewById()、setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等,通过 Data Binding , 我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的毫无营养的代码了。
DataBinding 是一个support库,最低支持到Android 2.1(API Level 7+)

简而言之,Data Binding,顾名思义,数据绑定,是Google对MVVM在Android上的一种实现,可以直接绑定数据到xml中,并实现自动刷新。现在最新的版本还支持双向绑定,尽管使用场景不是那么多。

Data Binding可以提升开发效率(节省很多以往需要手写的java代码),性能高(甚至超越手写代码),功能强(强大的表达式支持)。

内容有些乱,这几天先把学习的内容加进来,之后再做整理

MVVM Light

概述

MVVM Light Toolkit是一个Android MVVM 轻量级工具库,主要目的是更快捷方便的构建Android MVVM应用程序,工具库添加了一些Data Binding 不支持的属性,还有添加对控件事件的封装,同时提个一个全局消息通道方便ViewModel 之间的通信,Toolkit主要包括两部分Binding和Messenger

在View和ViewModel的绑定中,包含两种绑定,一种是数据的绑定(比如:TextView:text),另外一种命令绑定,命令绑定我们可以理解为事件绑定,(比如:Button:click),但是目前Databinding 并不完全支持命令的绑定, 而且对Data的绑定的支持也不完善(比如说不支持AdapterView对DataSource的绑定),那么在MVVM LightBinding我们添加了部分Data绑定支持和Command绑定的支持。

Data 绑定
我们添加了(ImageView:uri、placeholderImageRes),(ListView:views、itemView) 等部分控件的一些属性,这使得我们用起来就方便很多,例如我们只要在xml布局标签中设置uri,那么这个图片就能自动去加载这个uri的图片,如下:

 <ImageView    
 android:layout_width="match_parent"  
 android:layout_height="match_parent"  
 android:layout_alignParentRight="true"     
 bind:uri="@{viewModel.imageUrl}"
 bind:placeholderImageRes="@{R.drawable.ic_launcher}"/>

Command 绑定
Command 翻译为命令,就是控件发号施令,然后有人去回复处理这个命令,比如Button 发出一个Click Command,那么应该有个处理者来处理这个命令,我们把这个处理者叫做ReplyCommand,我们把ReplyCommand绑定到相应的控件上,如果控件发出Event的时候,就会找到这个ReplyCommand让它来处理这个事件,说得简单一些就是我们对控件一些事件做了简单的封装,使得处理这些事件我们只有关注我们相应事件传递给我们想要的数据,其他UI相关的逻辑并不需要我们去关心。接下来我们简要说明具体使用方法:

 <android.support.v7.widget.RecyclerView
          android:id="@+id/recyclerView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          bind:onLoadMoreCommand="@{viewModel.loadMoreCommand}"/>

bind:onLoadMoreCommand: RecyclerView 在滑到最底部的时候自动触发这个事件,这个事件的处理者是一个ReplyCommand类型的变量
接下来我们在ViewModel 里面的定义一个ReplyCommand 变量

public final ReplyCommand<Integer> loadMoreCommand = new ReplyCommand<>(
  (count) -> {
      /*count 代表当前RecyclerView 有多少个Item,通过这个值我们可以
        得到当前应该去加载第几页的数据*/
       int page=count / LIMIT;
       loadData(page)
  });

对于处理RecyclerView 下拉加载更多,我们之前的做法可能需要去写各种代码来判断是否滑动底部,在OnScrollListener 里面做一些计算然后才知道滑到底部了,但是如果用封装好的Command事件,你的ViewModel里面的代码将非常简洁,只要简要的声明一个 ReplyCommand,它就能自动传一个当前List中Item的总数给你,你要做的只是专注你的业务处理(比如 加载数据),ViewModel 里面只处理数据,不处理控件,没有任何控件的引用,所以UI的状态和变化都由数据来绑定控制的,这样数据就成了主角,我们在ViewModel 里面只要和数据打交道就可以了。

<think>好的,用户现在问的是`torch.cuda.empty_cache()`是否只能清空一个GPU的缓存。我需要先回忆一下PyTorchCUDA内存管理的机制。 首先,用户之前遇到了关于Scikit-learn的分类指标警告和PyTorch的NCCL支持问题,现在转向了CUDA内存管理。可能用户正在处理多GPU训练,或者在单GPU使用中遇到了显存问题。 `torch.cuda.empty_cache()`的作用是释放当前由缓存分配器持有的未使用内存PyTorch默认情况下会缓存一部分内存以提高分配效率,这个函数可以释放那些未被使用的缓存内存。但用户的问题是,这个函数是否只影响当前的一个GPU。 根据PyTorch的文档,当使用多GPU时,每个GPU都有自己的缓存分配器。所以,如果用户有多个GPU,`torch.cuda.empty_cache()`只会释放当前设备(即通过`torch.cuda.current_device()`指定的那个)的缓存。要清空所有GPU的缓存,需要遍历每个设备,切换当前设备,然后分别调用该函数。 用户可能是在多GPU环境下运行代码,发现显存没有完全释放,或者遇到显存不足的问题。需要确认用户是否在使用多GPU,以及是否正确地切换了设备来清空每个GPU的缓存。 另外,用户可能对PyTorch内存管理机制不太熟悉,所以需要解释清楚这个函数的行为,以及在不同情况下的正确使用方法。可能还需要提醒用户,即使调用了这个函数,显存的释放也依赖于是否有未使用的缓存,实际使用的张量不会被释放。 最后,给出示例代码,说明如何为所有GPU清空缓存,并强调正确使用设备切换的重要性。还要提醒用户,显存管理需要结合其他方法,如手动删除变量、运行垃圾回收等,才能有效管理显存使用。</think>--- 在 PyTorch 中,`torch.cuda.empty_cache()` 的作用是释放当前 CUDA 设备(即当前选中的 GPU)的**未使用缓存内存**。其行为取决于 **GPU 设备的选择**和 **多 GPU 环境**的配置。以下是详细说明: --- ### **1.GPU 场景** - 如果你只有一个 GPU,或者代码中未显式指定 GPU 设备: `torch.cuda.empty_cache()` 会清空当前默认 GPU 的缓存内存。 例如: ```python import torch # 默认使用 GPU 0(仅单卡时) a = torch.randn(1000, 1000, device="cuda") # 占用显存 del a # 删除变量 torch.cuda.empty_cache() # 释放 GPU 0 的未使用缓存 ``` --- ### **2.GPU 场景** - 如果你有多个 GPU,且代码中显式切换了设备: 需要**依次选中每个 GPU 并单独调用** `empty_cache()`,才能清空所有 GPU 的缓存。 例如: ```python import torch # 清空 GPU 0 的缓存 torch.cuda.set_device(0) torch.cuda.empty_cache() # 清空 GPU 1 的缓存 torch.cuda.set_device(1) torch.cuda.empty_cache() ``` --- ### **3. 关键注意事项** 1. **缓存释放的范围**: `empty_cache()` **仅释放由 PyTorch 缓存分配器管理的未占用内存**,不会释放正在被张量占用的显存。 - 已分配的张量必须手动删除(如 `del tensor`)或超出作用域后,其显存才会被缓存分配器回收。 - 调用 `empty_cache()` 后,这些回收的内存才会真正释放回系统。 2. **多进程分布式训练**: 在分布式训练(如使用 `torch.distributed` 或 `DataParallel`)时,每个进程可能绑定到不同的 GPU。 - 每个进程需独立调用 `empty_cache()` 清理自己绑定的 GPU 缓存。 - 例如: ```python # 每个进程仅清理自己绑定的 GPU torch.cuda.empty_cache() ``` 3. **自动缓存管理**: PyTorch 默认会缓存部分显存以提高分配效率。频繁调用 `empty_cache()` 可能导致性能下降,建议仅在显存不足时手动调用。 --- ### **4. 验证显存释放** 可以使用 `torch.cuda.memory_summary()` 或以下代码查看显存状态: ```python import torch # 查看当前 GPU 的显存使用情况(单位:字节) print(torch.cuda.memory_allocated()) # 当前已分配的显存 print(torch.cuda.memory_reserved()) # 当前缓存分配器保留的显存(包括未使用的) ``` --- ### **总结** | 场景 | 行为 | |------------|----------------------------------------------------------------------| | **单 GPU** | 清空当前 GPU 的未使用缓存。 | | **多 GPU** | 需遍历所有 GPU,分别调用 `empty_cache()` 才能清空每个设备的缓存。 | --- ### **最佳实践** - 显存不足时手动调用 `empty_cache()`,但避免在循环中频繁使用。 - 结合显存监控工具(如 `nvidia-smi` 或 PyTorch 内置函数)诊断显存泄漏。 - 多 GPU 场景显式指定设备并分别清理: ```python for i in range(torch.cuda.device_count()): torch.cuda.set_device(i) torch.cuda.empty_cache() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_Boss_Hao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值