在Android Architecture Components(AAC)中ViewMode是为界面组件提供数据并可在界面配置更改后继续存在的对象。例如界面的旋转导致界面配置信息改变。
对于为界面提供数据,我们所知道的也有其他的一些模式,例如MVP的Presenter与MVVM中的ViewModel。那么我们进行一个假设,如果Activity发生界面旋转,此时上述的提供数据的模式会发生什么呢?
对于以上问题,ViewModel都能够帮我们解决。只要Activity没有彻底被销毁,使用的都是同一个ViewModel,同时对于它的创建与销毁我们无需进行维护管理,能很好的保证资源的释放。
下面的这张图能够帮助我们更好的观察它在Activity中的生命状态
ViewModel贯穿Activity的整个生命周期,只有当Activity彻底释放时才会将其销毁。所以它能够更好的帮助我们实现持久化数据,防止不必要的数据请求,提高App的性能。
是不是有点好奇了呢,下面我们来简单介绍它的使用,为什么说简单呢?因为真的很简单…
如果你已经有了解过上篇关于Lifecycle的文章(Android Architecture Components Part3:Lifecycle),那么可以直接跳过依赖部分,没有的我们继续。
在使用ViewModel之前,我们需要在App或者Module的build.gradle中添加如下代码
1dependencies {
2 def lifecycle_version = "1.1.1"
3
4 // ViewModel and LiveData
5 implementation "android.arch.lifecycle:extensions:$lifecycle_version"
6 annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
7}
依赖已经准备完毕,我们可以直接通过如下代码使用
1class ContactsViewModel(application: Application, private val defTitle: String = "Contacts") : AndroidViewModel(application) {
2 val message: MutableLiveData<String> by lazy { MutableLiveData<String>() }
3 val contactsList: MutableLiveData<List<ContactsModel>> = MutableLiveData()
4 ....
5 ....
6
7 fun getContacts(refresh: Boolean): LiveData<List<ContactsModel>> {
8 message.value = ""
9 if (refresh) {
10 getDataFromRemote()
11 } else if (contactsList.value == null || contactsList.value?.size ?: 0 <= 0) {
12 message.value = "数据请求中,请稍后!"
13 if (mLocalData.isEmpty()) {
14 getDataFromLocal()
15 }
16 }
17 return contactsList
18 }
19 ...
20 ...
21}
我们创建ContactsViewModel,让它继承于AndroidViewModel,它是对抽象ViewModel的扩展,使用它时需要传入Application对象,方便一些资源的获取。由于ViewModel的特性是对数据进行持久化,所以它不能持有与Activity相关的引用(Context),防止内存泄露,因此这里使用与应用生命周期绑定的Application。
在ContactsViewModel中我们结合MutableLiveData来更好的管理数据的变化更新。
ViewModel创建好了,接下来只剩下在Activity中进行使用。
1class ContactsActivity : AppCompatActivity() {
2
3 private lateinit var mViewModel: ContactsViewModel
4
5 override fun onCreate(savedInstanceState: Bundle?) {
6 super.onCreate(savedInstanceState)
7 setContentView(R.layout.activity_contacts_layout)
8 setupViewModel()
9 }
10
11 private fun setupViewModel() {
12 mViewModel = ViewModelProviders.of(this)[ContactsViewModel::class.java]
13 //active STARTED、RESUMED
14 mViewModel.getContacts(true).observe(this,
15 Observer {
16 //todo ...
17 })
18 mViewModel.message.observe(this,
19 Observer {
20 //todo ...
21 })
22 }
23}
实际上我们只需一行代码就可以获取到ViewModel的实例,通过ViewModelProviders.of()方法传入Activity对象,它会返回一个ViewModelProvider对象,然后我们再使用它的get()来根据不同的ViewModel的Class对象来获取到相应的ViewModel实例。
只要Activity对象没有改变,同时都是同一个ViewModel的Class对象,那么我们无论何时获取的都是同一个ViewModel实例。这就实现了在Activity中的ViewModel持久化特性。由于ViewModel是同一个,自然它里面的数据也是同一份。
得到ViewModel后,剩下的就是对数据的操作与响应。这里结合了LiveData所以方便了许多。
LiveData之间已经有详细介绍,如需了解可以查看文章末的链接。
到这里我想你心中可能会有如下几个疑问
这两个疑问都将由ViewModelProvider来解决。
我们回到获取ViewModelProvider的ViewModelProviders.of()方法,进入源码查看。
1 @NonNull
2 @MainThread
3 public static ViewModelProvider of(@NonNull FragmentActivity activity,
4 @Nullable Factory factory) {
5 Application application = checkApplication(activity);
6 if (factory == null) {
7 factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
8 }
9 return new ViewModelProvider(ViewModelStores.of(activity), factory);
10 }
我们也没有发现我们想要的代码,但我们发现factory。我们在获取ViewModel时并没有传入factory,所以它会走空判断里面的代码,创建一个默认的factory。那么我们再进入ViewModelProvider中查看静态内部类AndroidViewModelFactory
1 public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
2
3 ...
4 ...
5
6 @NonNull
7 @Override
8 public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
9 if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
10 //noinspection TryWithIdenticalCatches
11 try {
12 return modelClass.getConstructor(Application.class).newInstance(mApplication);
13 } catch (NoSuchMethodException e) {
14 throw new RuntimeException("Cannot create an instance of " + modelClass, e);
15 } catch (IllegalAccessException e) {
16 throw new RuntimeException("Cannot create an instance of " + modelClass, e);
17 } catch (InstantiationException e) {
18 throw new RuntimeException("Cannot create an instance of " + modelClass, e);
19 } catch (InvocationTargetException e) {
20 throw new RuntimeException("Cannot create an instance of " + modelClass, e);
21 }
22 }
23 return super.create(modelClass);
24 }
25 }
我们只看关键代码,它继承于NewInstanceFactory,其中只有一个方法create()。AndroidViewModelFactory重新实现了create(),在重写的方法中我们找到了我们想要的方法调用。在这里它通过Class的getConstructor()方法获取匹配的Constructor对象,然后再通过newInstance()方法来获取匹配的对象实例。这样我们所需要的ViewModel实例就创建了,第一个疑问就此解决。
至于第二个疑问,细心的话不难发现,上面在调用newInstance()方法时已经传了一个初始化的参数mApplication。所以如果我们要再传入其它自定义的初始化参数,只需实现我们自己的create()方法。要自定义create()方法,我们就要自定义一个factory,继承NewInstanceFactory类。
下面是实现ContactsViewModel在初始化时传入特定的title值
1class ContactsFactory(private val application: Application) : ViewModelProvider.NewInstanceFactory() {
2
3 companion object {
4 @SuppressLint("StaticFieldLeak")
5 private var instance: ContactsFactory? = null
6
7 fun getInstance(application: Application): ContactsFactory {
8 if (instance == null) {
9 instance = ContactsFactory(application)
10 }
11 return instance as ContactsFactory
12 }
13 }
14
15 override fun <T : ViewModel?> create(modelClass: Class<T>): T {
16 if (modelClass.isAssignableFrom(ContactsViewModel::class.java)) {
17 return ContactsViewModel(application, "Factory Contacts") as T
18 }
19 return super.create(modelClass)
20 }
21}
重点自然是create()方法,通过isAssignableFrom()方法判断需要实例化的类型,由于我们能够明确到具体的类,所以可以直接使用正常的类实例化操作。已经有了factory,最后在获取ViewModel时传入即可
1mViewModel = ViewModelProviders.of(this, ContactsFactory.getInstance(application))[ContactsViewModel::class.java]
ViewModel就是这么简单,但它对数据的持久化却异常突出。是否已经迫不及待了呢?赶紧来试试它的特性吧。
最后Android Architecture Components(AAC)系列到此就告一段落了,让我们来回顾一下AAC。我们通过Room可以快速方便的实现本地数据存储;结合LiveData来观测数据的更新变化与及时反映到UI层;同时使用Lifecycle来让我们的组件或数据容器的具备生命感知能力,帮助我们的减少生命状态的处理与异常错误的发生;最后将界面数据存储到ViewModel中,使得数据达到持久化,减少不必要的数据请求与资源消耗。
下面的能够初步体现使用AAC后的App项目架构形态
最后感谢大家对AAC架构系列的支持!如果感觉不错的话,可以帮忙点赞转发一下,谢谢!同时文章中的代码都可以在Github中获取到。使用时请将分支切换到
feat_architecture_components
项目地址:
https://github.com/idisfkj/android-api-analysis
Android Architecture Components Part1:Room Android Architecture Components Part2:LiveData Android Architecture Components Part3:Lifecycle