• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Android:MVVM架构:ViewModel + LiveData + DataBinding

武飞扬头像
mr_zengkun
帮助1

前言

很早前写过一篇MVVM架构的文章,当时写的很粗糙,一直想抽空补全一下,自己对MVVM的理解,写一篇让新手都能够容易掌握的文章。
众所周知,Google已经开始倾向MVI架构,但是作为一个开发者,在开发中,只有存在合适的架构,所以关于MVI架构,希望以后有真正的了解再来写一写自己的见解。

基本使用

刚学习MVVM,这张架构图是非常重要的
学新通

  1. 简单的说一下
    ViewModel:关联层,将Model和View进行绑定,只做和业务逻辑相关的工作,不涉及任何和UI相关的操作,不持有控件的引用,不更新UI。
    View:视图层,只做和UI相关的工作,不涉及任何业务逻辑,不涉及操作数据,不处理数据。UI和数据严格的分开。
    Model:模型层,保存数据的状态,比如数据存储、网络请求。同时还与View存在一定的耦合,可以通过观察

  2. 启用方式

//添加ViewModel的引用
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'



//启用DataBinding
buildFeatures {
   dataBinding true
}
  1. 自定义的VM类只要继承引用的第三方库中的ViewModel抽象类即可
class MyViewModel: VideModel{
}
  1. 然后我们就可以在activity/fragment中实例化它
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
// 这里需要提到一下在旧版本的APIVIewModelProviders已经被添加了Deprecated标签,也就是已经不推荐使用了

viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

ViewModel的优势在于生命周期和数据的持久化,这就是为什么我们需要在activity/fragment去使用它。其次就是异步回调,不会造成内存泄漏问题,对View层和Model层的完全解耦,这就是ViewModel在整个MVVM架构中的重要性了。

ViewModel生命周期

学新通
Google官方给的生命周期图

  1. 生命周期长于Activity。不难发现,ViewModel的生命周期,贯穿整个被绑定的Activity的生命周期,也就是说,既然Activity因为系统配置变更销毁重建,比如旋转屏幕等,ViewModel对象依旧会被保留,并关联到新的Activity。只有当Activity正常销毁时,ViewModel对象才会调用onCleared()进行清除。
  2. 开头说过,众多开发模式当中,他们的区别大同小异,都是为了实现解耦。而我们知道,在MVP的Presenter中,需要持有View的接口对象,来回调结果给界面。而ViewModel是不需要持有UI层的引用,由于ViewModel贯穿了Activity的整个完整的生命周期,甚至比Activity生命周期要长,这就导致,如果让ViewModel持有Activity的引用时,容易造成内存泄漏等问题。

Tip:当ViewModel一定需要context引用,有没有什么办法呢? 其实是有的, ViewModel抽象类有一个实现类AndroidViewModel(Application),它接收的对象是application。

这是一个登录界面使用ViewModel的例子
学新通
仔细看会发现,实战当中,其实就是在mainViewModel定义了两个属性,用来缓存account 和 pwd,但是这两个数据却能持久化在这里面。为什么呢?因为当你旋转屏幕的时候,你会发现输入框的值依旧存在。我们知道在做横竖屏切换的时候,activity会被重新创建,此时如果我们数据是放在activity当中,数据就已经被销毁了。

LiveData

LiveData数据变化感知,也就是说当我们对一个对象多次赋值的时候,可以通过LiveData去操作,监听对象改变,然后处理相应的业务逻辑。

ViewModel.kt

public MutableLiveData<String> account = new MutableLiveData<>();
public MutableLiveData<String> pwd = new MutableLiveData<>();

account.setValue("admin")
pwd.setValue("123456")

这里使用的是MutableLiveData,表示值的内容可变动,而LiveData是不可变的。<>中的是泛型,你可以直接将一个对象放进去,当对象的内容有改动时,通知改变就可以了。

MainActivity.kt

viewModel.account.observe(this, {
	tvAccount.text = it
})
viewModel.pwd.observe(this, {
	tvPwd.text = it
})

我们可以看到,在viewModel中通过对MutableLiveData对象调用它的setValue,在Activity中,就可以通过MutableLiveData的observe监听到数据的变化,从而处理相应的逻辑。(例子代码中是通过TextView进行显示)
在上述例子中,可以看到我们使用的是setValue()的方式,还有一种方式是postValue(),需要注意一点,setValue()的方式只允许在主线程中使用,而postValue可以在任何线程中使用,并且如果在主线程执行发布之前,多次调用此方法,则只会分派最后一个值

DataBinding

Android的DataBinding已经内置了,因此只需要在app模块的build.gradle中开启就可以使用了。

dataBinding {
        enabled = true
    }

Databinding顾名思义就是数据绑定,接下来会通过几个例子讲一下不同的绑定方式。

单向绑定

布局文件,当我们启动了DataBinding,在定义布局的根节点,通过AS的提示快捷按键,就可以显示添加data binding。
学新通

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <!--绑定数据-->
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>
    <LinearLayout>
    	<TextView
                android:id="@ id/tv_account"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user.account}" />
        <TextView
                android:id="@ id/tv_pwd"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user.pwd}" />
    </LinearLayout>
</layout>
学新通

最好进入Activity,在onCreate方法中,添加绑定

override fun onCreate(Bundle saveInstanceState) {
	val dataBinding = DataBindingUtil.setContentView(this, R.layou.activity_main)
	user = User("admin", "123456")
	dataBinding.setUser(user)

	/**
	我们可以通过手动赋值,比如外界输入等方式,给user赋值,在界面上就能及时显示修改的数据
	user.setAccount("study")
	user.setPwd("666")
	**/
}

双向绑定

DataBinding的双向绑定其主要的差别就是在布局文件中使用中,用@={viewModel.user.account}将数据直接赋值给数据源

    <!--绑定数据-->
    <data>
        <variable
            name="viewModel"
            type="com.example.viewmodels.MainViewModel" />
    </data>
		<TextView
                android:id="@ id/tv_account"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{viewModel.user.account}" />
        <TextView
                android:id="@ id/tv_pwd"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{viewModel.user.pwd}"/>
		<EditText
                android:id="@ id/et_account"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white"
                android:text="@={viewModel.user.account}"
                android:hint="账号" />
        <EditText
                android:id="@ id/et_pwd"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white"
                android:text="@={viewModel.user.pwd}"
                android:hint="密码"
                android:inputType="textPassword" />
学新通

这里需要注意几点:
第一是数据源,这里绑定的是viewModel,也就是MainViewModel中的数据都可以拿到。
第二就是响应的地方,通过这种方式去显示ViewModel中对象的变量数据在空间上,也就是两个TextView。
第三个地方,也就是双向绑定的意义,就是UI改变数据源。我们知道当输入框输入数据时,text属性值会改变为输入的数据,而@={viewModel.user.account}就是将输入的数据直接赋值给数据源。这样我们就不需要在Activity中再去处理EditText的内容了,减少了耦合。

而实际开发过程中,我们需要用到双向绑定的地方还是比较少的,相对于单项绑定。

如何在dialog或者自定义View中使用

我们知道ViewModel能在Activity和Fragment里使用,因此也能作为媒介使得Activity和Fragment进行交互。那么需要在View里使用呢?
假如我有一个自定义view或者dialog,它包含一堆数据和状态,能否使用ViewModel去管理数据呢?
先来看看ViewModel的源码,为什么说到ViewModel创建不建议通过new,而是ViewModelProvider的方式。

// ViewModelProvider.java
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
 // 注释 6, 通过ViewModelStore 拥有者的 getDefaultViewModelProviderFactory方法获取工厂对象
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : ViewModelProvider.NewInstanceFactory.getInstance());
    }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull ViewModelProvider.Factory factory) {
        //注释7,  保存工厂 Factory和 缓存对象,ViewModelStore
        mFactory = factory;
        mViewModelStore = store;
    }
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();,
        return get(DEFAULT_KEY   ":"   canonicalName, modelClass);
    }
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //注释 9, 从缓存 mViewModelStore中取 ViewModel的对象
        // Key值是 “androidx.lifecycle.ViewModelProvider.DefaultKey”   类名
        ViewModel viewModel = mViewModelStore.get(key);
        if (modelClass.isInstance(viewModel)) {
            ......
            // 是响应类的对象则直接返回
            return (T) viewModel;
        }
        ......
        ......
        //注释 10, 缓存中没有 ViewModel对象,用工厂创建,并存入缓存
        viewModel = (mFactory).create(modelClass);
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
学新通

从源码可以看出,ViewModel对象是通过工厂模式进行创建的,并且保存了Factory和缓存对象,当我们通过ViewModelProvider(this).get()方法获取ViewModel对象时,会优先通过缓存获取。

在看看注释6处的getDefaultViewModelProviderFactory()方法

   // ComponentActivity.java
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        ......
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    //注释 11, 获取了 Application对象,创建SavedStateViewModelFactory对象
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

    // SavedStateViewModelFactory.java
    public SavedStateViewModelFactory(@NonNull Application application,
                                      @NonNull SavedStateRegistryOwner owner,
                                      @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        //注释 12, 创建 AndroidViewModelFactory工厂
        mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        ......
       // 注释 13,通过 AndroidViewModelFactory创建 ViewModel对象
        return mFactory.create(modelClass);
        ......
    }
    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
        private static ViewModelProvider.AndroidViewModelFactory sInstance;
        @NonNull
        public static ViewModelProvider.AndroidViewModelFactory getInstance(@NonNull Application application) {
            // 注释14, 单例模式创建工厂实例
            if (sInstance == null) {
                sInstance = new ViewModelProvider.AndroidViewModelFactory(application);
            }
            return sInstance;
        }
        private Application mApplication;
        // 传入 Application对象
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                try {
                    //注释 15, 工厂方法设计模式创建 ViewModel对象,持有 Application的引用
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                }
                ......
            }
            return super.create(modelClass);
        }
    }
学新通

能看出AndroidViewModelFactory这个工厂,用单例模式创建了该工厂对象,然后使用工厂方法设计模式创建了ViewModel对象,ViewModel创建时传入的时Application参数,这里能够知道ViewModel对象最终持有的,并不是Activity的引用,而是Application的引用,也就是说,为什么ViewModel不容易造成内存泄漏的问题了。
到了这里,为什么不推荐使用new 去创建ViewModel也就迎刃而解了。

自定义view中,如果需要使用ViewModel,虽然官方不建议使用new ViewModel的方式,但是我们还是可以取巧,通过这种方式在view当中去使用它。- -!

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbhegh
系列文章
更多 icon
同类精品
更多 icon
继续加载