关于使用 Android MVVM + LiveData 模式的一些建议

ViewModels 在配置改变时仍然存在

将 View 的引用(Activity 或 Fragment)传递到 ViewModel 是一个严重的风险。假设 ViewModel 中请求了网络数据,数据在之后的一段时间返回。此时,View 的引用有可能被回收或者旧的 Activity 不再可见,这样就产生了内存泄漏,甚至崩溃。

避免在 ViewModels 中引用 Views

在 ViewModels 和 Views 中沟通推荐的方式是观察者模式,利用 LiveData 或者其他库提供的可观察的方式。

观察者模式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过观察者模式,在 Android 展示层中让 View(Activity 或 Fragment)观察(订阅改变) ViewModel 显得非常方便。因为 ViewModel 不需要知道 Android 内容,它也不知道 Android 是如何频繁地杀死 View 的。好处在于:

  1. ViewModels 在配置改变时依然存在,因此当旋转屏幕时不需要再次查询内部数据(数据库或网络)。
  2. 当一个长时间的操作结束时,在 ViewModel 中可观察的部分更新了。无论数据是否被观察了,当尝试更新不存在的 View 时没有空指针异常的发生。
  3. ViewModels 不引用 View,所以减少了内存泄漏的风险。

private void subscribeToModel() {
// Observe product data
viewModel.getObservableProduct().observe(this, new Observer() {
@Override
public void onChanged(@Nullable Product product) {
mTitle.setText(product.title);
}
});
}

在 UI 中注册数据推送,让 UI 观察它的改变。

臃肿的 ViewModels

如果你的 ViewModel 中放了太多代码或者太多的职责,可以考虑:

  • 将一些逻辑移动到 presenter,它和 ViewModel 有相同的范围。它可以和你 app 中的其他部分沟通并更新 ViewModel 中 LiveData 的持有者。
  • 添加一个领域层并采取简洁的架构,这样有利于测试和维护,也同样促进了快速脱离主线程。在 架构蓝图中有一个简洁架构的样例。

必要时分散职责,增加领域层。

使用数据仓库

在 App 架构指南中,可以看到大多数应用都有多种数据来源,比如:

  1. 远程:网络或云
  2. 本地:数据库或文件
  3. 内存缓存

在你的应用中拥有数据层是一个好主意,展示层完全注意不到。保留缓存、同步数据库和网络的算法都是无关紧要的。拥有一个完全隔离的仓库类作为一个单一的入口来处理这些复杂的事情是非常推荐的。

如果你有多种并且不同的数据模型,考虑添加多种仓库。

添加一个数据仓库作为访问数据的单一入口。

处理数据状态

考虑这个场景:你正在观察 ViewModel 中暴露的 LiveData,它包含一个展示项目的列表。那么在数据加载,网络错误或者空列表时,视图该如何呈现这些变化呢?

  • 你可以在 ViewModel 中暴露一个 LiveData。例如,MyDataState 应该包含那些数据是否正在加载,或者加载成功或加载失败的信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可以把数据包裹在一个保存了状态或其他元数据(例如一条错误消息)的类中。看看我们样例中的 Resource 类。

使用一些包裹类或另一个 LiveData 来暴露你数据的信息。

保存 Activity 状态

Activity 状态是一些当 Activity 消失时(意味着被回收或进程被杀死)你需要重新恢复屏幕的信息。旋转屏幕是一个最显著的案例,还好我们有 ViewModel。状态保存在 ViewModel 中是安全的。

然而在某些场景中,当 ViewModel 也消失时,你可能也需要恢复状态。比如当操作系统的资源紧缺时,可能会杀死你的进程。

为了高效地保存和恢复 UI 状态,需要结合持续性,onSaveInstanceState() 以及 ViewModels。

看看例子:ViewModels:持续,onSaveInstanceState(),恢复 UI 状态和装载器

事件

一个事件指发送一次的动作。ViewModels 暴露了数据,但什么是事件呢?例如,导航事件或者展示 Snackbar 消息都是应该只执行一次的动作。

事件的概念并不能很好的展示 LiveData 是如何存储和恢复数据的。来看一下下面的 ViewModel:

LiveData snackbarMessage = new MutableLiveData<>();

一个 Activity 开始观察这个数据,并且 ViewModel 完成了一个操作之后它需要更新这条消息:

snackbarMessage.setValue(“Item saved!”);

Activity 收到这条消息,并展示在 Snackbar 中。这显然没毛病。

然而,如果用户旋转手机,创建了新的 Activity 并开始观察。当 LiveData 观察发生后,Activity 立即收到了旧的值,这时消息再次展示了!

我们扩展了 LiveData,并创建了一个类叫 SingleLiveEvent,作为刚刚问题的解决方案。它仅仅发送订阅之后出现的更新。注意它只支持一个观察者。

为像导航或 Snackbar 消息等事件使用可观察的行为如 SingleLiveEvent

泄露 ViewModels

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

资源分享

  • 最新大厂面试专题

这个题库内容是比较多的,除了一些流行的热门技术面试题,如Kotlin,数据库,Java虚拟机面试题,数组,Framework ,混合跨平台开发,等

  • 对应导图的Android高级工程师进阶系统学习视频
    最近热门的,NDK,热修复,MVVM,源码等一系列系统学习视频都有!

1710687241042)]

  • 对应导图的Android高级工程师进阶系统学习视频
    最近热门的,NDK,热修复,MVVM,源码等一系列系统学习视频都有!

[外链图片转存中…(img-SrHgPbR8-1710687241043)]

下载方法:点赞+关注后 点击【Android高级工程师进阶学习】即可领取!

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的示例: 首先,在build.gradle文件中添加以下依赖项: ```groovy // ViewModel and LiveData implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' // Data Binding implementation 'androidx.databinding:databinding-runtime:4.0.1' ``` 接下来,创建一个名为MainActivity的Activity,并在其布局文件中添加两个Fragment的占位符: activity_main.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"/> </layout> ``` MainActivity.java: ```java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); // 加载第一个Fragment getSupportFragmentManager().beginTransaction() .replace(R.id.container, new FirstFragment()) .commit(); } } ``` 接下来,创建一个名为FirstFragment的Fragment,并在其布局文件中使用DataBinding绑定数据: first_fragment.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" android:onClick="@{viewModel::onNextClicked}" /> </LinearLayout> </layout> ``` FirstFragment.java: ```java public class FirstFragment extends Fragment { private FirstViewModel viewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用DataBinding绑定布局文件 FirstFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.first_fragment, container, false); // 创建ViewModel实例 viewModel = ViewModelProviders.of(this).get(FirstViewModel.class); // 将ViewModel与布局文件中的变量绑定 binding.setViewModel(viewModel); // 设置LifecycleOwner,以便LiveData知道何时更新UI binding.setLifecycleOwner(this); return binding.getRoot(); } } ``` 下面是FirstViewModel.java: ```java public class FirstViewModel extends ViewModel { private MutableLiveData<String> textLiveData = new MutableLiveData<>(); public FirstViewModel() { // 初始化LiveData的默认值 textLiveData.setValue("Hello, World!"); } public LiveData<String> getText() { return textLiveData; } public void onNextClicked() { // 更新LiveData的值 textLiveData.setValue("Next Clicked!"); } } ``` 最后,创建另一个名为SecondFragment的Fragment,并在MainActivity中添加一个方法来加载它: second_fragment.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Previous" android:onClick="@{viewModel::onPreviousClicked}" /> </LinearLayout> </layout> ``` SecondFragment.java: ```java public class SecondFragment extends Fragment { private SecondViewModel viewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用DataBinding绑定布局文件 SecondFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.second_fragment, container, false); // 创建ViewModel实例 viewModel = ViewModelProviders.of(this).get(SecondViewModel.class); // 将ViewModel与布局文件中的变量绑定 binding.setViewModel(viewModel); // 设置LifecycleOwner,以便LiveData知道何时更新UI binding.setLifecycleOwner(this); return binding.getRoot(); } } ``` 下面是SecondViewModel.java: ```java public class SecondViewModel extends ViewModel { private MutableLiveData<String> textLiveData = new MutableLiveData<>(); public SecondViewModel() { // 初始化LiveData的默认值 textLiveData.setValue("Goodbye, World!"); } public LiveData<String> getText() { return textLiveData; } public void onPreviousClicked() { // 更新LiveData的值 textLiveData.setValue("Previous Clicked!"); } } ``` 最后,在MainActivity中添加一个方法来加载SecondFragment: ```java private void loadSecondFragment() { getSupportFragmentManager().beginTransaction() .replace(R.id.container, new SecondFragment()) .commit(); } ``` 现在,你的MVVM Android项目就完成了!你可以使用LiveData和ViewModel来管理数据,并使用DataBinding将数据绑定到UI上。Lifecycle组件可确保UI在活动和片段之间正确地进行管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值