使用Kotlin构建MVVM应用程序—提高篇:ViewModel

栏目: 编程工具 · 发布时间: 7年前

内容简介:大家好,这里是使用Kotlin构建MVVM应用程序—提高篇:ViewModel。本篇文章将介绍google推荐的架构组件ViewModel的使用方法及实现原理。为什么?看到ViewModel这个名字相信都会联系到MVVM架构中的VM。

写在前面

大家好,这里是使用Kotlin构建MVVM应用程序—提高篇:ViewModel。

本篇文章将介绍google推荐的架构组件ViewModel的使用方法及实现原理。

为什么要有ViewModel?

为什么?看到ViewModel这个名字相信都会联系到MVVM架构中的VM。

但是在我看来,这两者并非是一个意思。如果你想实现MVVM架构的APP,按照

使用Kotlin构建MVVM应用程序 基础篇的内容就已经足够了。

而我推测google把它称为ViewModel的原因可能有两点:

  1. ViewModel架构组件是为VM层服务的。
  2. 容易联想到MVVM架构,代表着google更推荐Android工程师们应用MVVM架构,而并非冗杂繁复的MVP。

当然这些是题外话。既然不使用ViewModel也能构建MVVM应用,那么ViewModel是来做什么的呢?

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

简单说来,ViewModel是用来存储和管理UI相关的数据,将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期, 如当屏幕旋转Activity重建后,ViewModel中的数据依然有效 。它还可以帮助开发者轻易实现 FragmentFragment 之间, ActivityFragment 之间的 通讯以及共享数据

其中最具有吸引力的功能就是 屏幕旋转Activity重建后,ViewModel中的数据依然有效 。遥想当年,屏幕旋转当真是开发者不得不迈过的槛。而现在很多应用都只要求竖屏或者强制竖屏,不得横屏,所以对于这样的应用,我的建议是可以不用ViewModel组件,按照普通的VM开发就可以了。当然适当了解一下还是可以的。

ViewModel快速开始

首先我们需要在app/build.gradle加入相应的依赖

//ViewModel
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:viewmodel:1.1.1"
kapt "android.arch.lifecycle:compiler:1.1.1"

然后,让你的VM层继承ViewModel组件提供的 ViewModel 类,如果需要用到 Applicationcontext ,那么就继承 AndroidViewModel 类。

class PaoViewModel @Inject constructor(private val repo: PaoRepo) :ViewModel(){
    //...
}

在View层的 PaoActivity 之中,以前的 mViewModel 是通过Dagger2注入的,而现在需要进行一下修改

class PaoActivity : RxAppCompatActivity() {

    lateinit var mBinding : PaoActivityBinding

    lateinit var mViewModel : PaoViewModel

    @Inject
    lateinit var factory: ViewModelProvider.Factory

     override fun onCreate(savedInstanceState: Bundle?) {
         //....
        mViewModel=ViewModelProviders.of(this,factory).get(PaoViewModel::class.java)
        //...
     }

}

这里有两个概念 ViewModelProvider.FactoryViewModelProviders 。再继续之前,我们需要先了解如何打造自己的 ViewModelProvider.Factory

ViewModelProvider.Factory

看到这个名字,自然就联想到工厂模式,这里提供我们需要的mViewModel实例。当然ViewModel没有Dagger2那么神奇,不会帮我们自动生成,所以需要我们自己来实现需要的 ViewModelProvider.Factory

class PaoViewModelFactory : ViewModelProvider.Factory{
    //需要实现create方法,返回具体的viewmodel
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        //return T
    }
}

那怎么创建具体的实例呢?可以在这里一个个new出相应的依赖,但既然已经有Dagger2这么棒的依赖管理工具,当然是使用Dagger2了。

@Singleton
class PaoViewModelFactory @Inject constructor(private val viewModel:PaoViewModel): ViewModelProvider.Factory{
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isInstance(viewModel)){
            return viewModel as T
        }else{
            throw IllegalArgumentException("unknown model class $modelClass")
        }
    }

}

到此,当然还没有结束。这样的写法只针对单个ViewModel,如果有多个呢?我们自然不希望出现如下的代码

@Singleton
class PaoViewModelFactory @Inject constructor(private val viewModel:PaoViewModel,private val viewModelOne:OneViewModel,private val viewModelOther:OtherViewModel): ViewModelProvider.Factory{
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return when{
            modelClass.isInstance(viewModel) -> viewModel as T
            modelClass.isInstance(viewModelOne) -> viewModelOne as T
            modelClass.isInstance(viewModelOther) -> viewModelOther as T
            //...
            else -> throw IllegalArgumentException("unknown model class $modelClass")
        }
    }

}

这样费力不太好并且没有效率的事当然不是有(想)效(偷)率(懒)的 程序员 喜欢做的事。

简单的做法是注入一个map,value是我们需要的ViewModel实例,key值为相应的Class。

幸运的是通过Dagger2的 @IntoMap 可以为我们自动构造所需的map对象,是不是对Dagger2开始爱不释手了^_^

@Module
abstract class ViewModelModule{

    @Binds
    @IntoMap
    @ViewModelKey(PaoViewModel::class)//自定义的mapKey
    abstract fun bindPaoViewModel(viewModel: PaoViewModel):ViewModel

//    @Binds
//    @IntoMap
//    @ViewModelKey(OtherViewModel::class)
//    abstract fun bindOtherViewModel(viewModel: OtherViewModel):ViewModel 
//      ...

    @Binds
    abstract fun bindViewModelFactory(factory:PaoViewModelFactory):ViewModelProvider.Factory
}

然后它放到AppComponent中

@Singleton
@Component(modules = arrayOf(
        AndroidInjectionModule::class,
        AppModule::class,
        ViewModelModule::class,
        ActivityModule::class)
)
interface AppComponent {}

最后修改 PaoViewModelFactory ,代码参考自 android-architecture-components/GitHubBrowserSample

@Singleton
class PaoViewModelFactory @Inject constructor(private val creators:Map<Class<out ViewModel>,Provider<ViewModel>>): ViewModelProvider.Factory{
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = creators[modelClass]?:creators.entries.firstOrNull{
            modelClass.isAssignableFrom(it.key)
        }?.value?:throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }

}

大功告成,现在我们的ViewModel已经具备了屏幕旋转Activity重建后,ViewModel中的数据依然有效的能力。

使用Kotlin构建MVVM应用程序—提高篇:ViewModel

rotate

原理剖析

首先我们先来看看ViewModel

```java

public abstract class ViewModel {

/**


以上所述就是小编给大家介绍的《使用Kotlin构建MVVM应用程序—提高篇:ViewModel》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Responsive Web Design

Responsive Web Design

Ethan Marcotte / Happy Cog / 2011-6 / USD 18.00

From mobile browsers to netbooks and tablets, users are visiting your sites from an increasing array of devices and browsers. Are your designs ready? Learn how to think beyond the desktop and craft be......一起来看看 《Responsive Web Design》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具