关注我的公众号 “安安安安卓” 收费学常识

大部分人更关怀用法,所以我先讲用法, 再讲对 viewmodel 的了解,最初讲源码

1、ViewModel 初始化形式

来到 androidx,ViewModel 的创立形式与老版本有了很大的不同,所以这里还是要将 Viewmodel 的初始化讲一下

1.1、安卓工厂初始化

每次都会从新创立 model,并且不受 ViewModelStore 管控,所以无非凡需要禁止应用该种形式

应用 AndroidViewModelFactory 工厂创立

  1. viewmodel 类定义
class AndroidFactoryViewModel(app:Application): AndroidViewModel(app) {    fun print(){        logEE("应用安卓默认工厂创立viewmodel")    }}
  1. 创立 viewmodel 实例代码
val model = ViewModelProvider.AndroidViewModelFactory.getInstance(application)                .create(AndroidFactoryViewModel::class.java)            model.print()

1.2、简略工厂初始化 ViewModel

每次都会从新创立 model,并且不受 ViewModelStore 管控,所以无非凡需要禁止应用该种形式

NewInstanceFactory

  1. viewmodel 类定义代码
class SimpleFactoryViewModel: ViewModel() {    fun print(){        logEE("应用简略工厂创立viewmodel")    }}
  1. 创立 viewmodel 代码
 val model =ViewModelProvider.NewInstanceFactory().create(SimpleFactoryViewModel::class.java) model.print()

1.3、自定义安卓工厂初始化

屡次创立能够复用 model,不会从新创立

默认的工厂只能创立带 Application 的 ViewModel 实例的,咱们通过自定义工厂实现自定义结构参数的目标

  1. 定义安卓工厂
class CustomAndroidViewModelFactory(val app: Application, private val data: String) :    ViewModelProvider.AndroidViewModelFactory(app) {    override fun <T : ViewModel?> create(modelClass: Class<T>): T {        if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {            return try {                modelClass.getConstructor(Application::class.java, String::class.java)                    .newInstance(app, data)            } ...        }        return super.create(modelClass)    }}

省略了 catch 中的代码,您能够到源码中查看

在咱们的自定义工厂的结构中传入一个自定义的 data 参数,代表咱们的自定义结构参数

  1. viewmodel 类定义

    必须是继承 AndroidViewModel 才能够

class CustomAndroidViewModel(private val app: Application, private val data: String) :    AndroidViewModel(app) {    fun print() {        logEE(data)    }}

咱们的 CustomAndroidViewModel 类中也有一个 data:String 参数,这个参数就是对应上一步中自定义工厂中的 data 参数

  1. 实例化 viewmodel
val model = ViewModelProvider(                viewModelStore,                CustomAndroidViewModelFactory(application, "自定义安卓工厂创立viewmodel")            ).get(CustomAndroidViewModel::class.java)            model.print()

1.4、自定义简略工厂初始化

屡次获取能够复用 model,不会从新创立

自定义简略工厂也是为了实现 viewmodel 结构传参的目标,废话不多说,间接上代码吧

  1. 定义安卓工厂
class CustomSimpleViewModelFactory(app:Application,private val data:String) : ViewModelProvider.AndroidViewModelFactory(app) {    override fun <T : ViewModel?> create(modelClass: Class<T>): T {        return try {            modelClass.getConstructor(String::class.java).newInstance(data)        } ......    }}
  1. viewmodel 类定义
class CustomSimpleViewModel(private val data: String) : ViewModel() {    fun print() {        logEE(data)    }}
  1. 实例化 viewmodel
val model = ViewModelProvider(                viewModelStore,                CustomSimpleViewModelFactory(application, "自定义简略工厂创立viewmodel")            ).get(CustomSimpleViewModel::class.java)            model.print()

1.5 应用委托机制创立 viewmodel(举荐)

屡次创立能够复用 model,不会从新创立

google 官网给咱们提供了 activity-ktx 库,通过它咱们能够应用委托机制创立 viewmodel

实现形式如下:

  1. 退出依赖
implementation 'androidx.activity:activity-ktx:1.2.2'
  1. 创立 viewmodel 类
class EnTrustModel : ViewModel() {    fun print(){        logEE("关注公众号 \"安安安安卓\" 收费学常识")    }}
  1. 实例化 viewmodel
private val wtModel: EnTrustModel by viewModels<EnTrustModel> {        ViewModelProvider.NewInstanceFactory()    }
  1. 调用 viewmodel 的办法
 wtModel.print()

2、 viewmodel 概览

ViewModel 类旨在以重视生命周期的形式存储和治理界面相干的数据。ViewModel 类让数据可在产生屏幕旋转等配置更改后持续留存。

咱们晓得 android 中的 Activity 和 Fragment 都是能够对生命周期进行治理的,如果 Activity/Fragment 被零碎销毁,例如屏幕旋转就必须从新创立 Activity/Fragment,这个时候就波及到一个数据恢复的问题。通常咱们会应用 onSaveInstanceState 和 onRestoreSaveInstance 两个办法对数据进行保留和复原,然而这两个办法只能解决数据量较小的状况。

如果是大数据量的状况那么用 viewmodel 就是不错的抉择了

同时 viewmodel 还能够帮忙咱们将业务逻辑从 Activity/Fragment 中分离出来

3、 viewmodel 实现 Fragment 之间数据共享

同一个 activity 中呈现多个 Fragment,并且这些 Fragment 之间须要进行通信,这样的需要是很常见的

以往咱们可能会采纳下列形式实现数据共享:

  1. EventBus
  2. 接口回调
  3. 全局共享数据池

然而当咱们学了 viewmodel 后就会发现,viewmodel 能够轻松实现 Fragment 之间的数据共享

咱们能够让多个 Fragment 共用同一个 ViewModel 实现数据的共享。

本例中咱们要实现上面的性能:

activity 中有两个 fragment,FragmentA 和 FragmentB,咱们在 ViewModel 的构造方法中开始倒计时 2000s,在 FragmentA 中察看倒计时的数据并展现在页面上。而后再切换到 FragmentB,如果 FragmentB 中的倒计时的秒数没有重置为 2000,阐明咱们的数据共享胜利了。

上代码:

  1. 创立 ViewModel
class ShareDataModel: ViewModel() {    val liveData = MutableLiveData<String>()    var total = 2000L    init {        /**         * 实现倒计时,一秒钟倒计时一次         */        val countDownTimer = object :CountDownTimer(1000 * total, 1000) {            override fun onTick(millisUntilFinished: Long) {                liveData.postValue("残余倒计时工夫 ${--total}")            }            override fun onFinish() {                logEE("倒计时实现")            }        }        countDownTimer.start()    }}
  1. 创立 FragmentA
class FragmentA : Fragment() {    private val model: ShareDataModel by activityViewModels()    override fun onCreateView(        inflater: LayoutInflater, container: ViewGroup?,        savedInstanceState: Bundle?    ): View? {        return inflater.inflate(R.layout.fragment_a, container, false).apply {            model.liveData.observe(viewLifecycleOwner,                { value -> findViewById<TextView>(R.id.tv_aresult).text = value })        }    }}
  1. 创立 FragmentB
class FragmentB : Fragment() {    private val model: ShareDataModel by activityViewModels()    override fun onCreateView(        inflater: LayoutInflater, container: ViewGroup?,        savedInstanceState: Bundle?    ): View? {        return inflater.inflate(R.layout.fragment_b, container, false).apply {            model.liveData.observe(viewLifecycleOwner,                { value -> findViewById<TextView>(R.id.tv_bresult).text = value })        }    }}
  1. 展现后果

通过 gif 咱们发现,即便 replace 后倒计时的数据依然没有扭转,阐明咱们胜利实现了数据共享

4、 对于 viewmodel 的几个知识点

  1. viewmodel 相对不能够援用视图
  2. 在 activity 的存在期间可能会屡次走 onCreate 办法,然而咱们的 viewmodel 只有 actvity 完结并销毁的状况下才会被回收
  3. viewmodel 通过 lifecycle 来察看 activity/fragment 的生命周期

5、源码解析

5.1、创立 ViewModel 源码解析

先看一下创立 ViewModel 的代码:

val model = ViewModelProvider(                viewModelStore,                CustomSimpleViewModelFactory(application, "自定义简略工厂创立viewmodel")            ).get(CustomSimpleViewModel::class.java)

那么就从 ViewModelProvider 构造方法和 get 办法说起吧,

  1. ViewModelProvider 构造方法

ViewModelProvider 构造方法传入两个参数,ViewModelStore(用来存储管理 ViewModel)和 Factory(创立 ViewModel 的工厂)

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {        mFactory = factory;        mViewModelStore = store;    }
  1. ViewModelProvider 的 get 办法
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {        String canonicalName = modelClass.getCanonicalName();        if (canonicalName == null) {            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");        }        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);    }

getCanonicalName 办法能够获取一个能惟一标识 Class<T>的字符串,最终会用作生成咱们 ViewModelStore 中存储 ViewModel 的 key。

获取 key 后咱们将 key 和 modelClass 做为参数调用重载 get 办法

  1. 重载 get 办法

办法的解读写在代码中了

 public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {        ViewModel viewModel = mViewModelStore.get(key);//从ViewModelStore中依据key获取ViewModel,如果有就会返回        if (modelClass.isInstance(viewModel)) {//判断获取的ViewModel是否是传入的modelClass类型            if (mFactory instanceof OnRequeryFactory) {//不看这里                ((OnRequeryFactory) mFactory).onRequery(viewModel);            }            return (T) viewModel;//如果是就返回viewmodel        } else {            //noinspection StatementWithEmptyBody            if (viewModel != null) {                // TODO: log a warning.            }        }        if (mFactory instanceof KeyedFactory) {            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);//个别不会用这个工厂        } else {            viewModel = mFactory.create(modelClass);//创立ViewModel        }        mViewModelStore.put(key, viewModel);//将ViewModel增加到mViewModelStore中        return (T) viewModel;    }

5.2、ViewModelStore 类解析

ViewModelStore 中有三个重要办法 get、put、clear,并且保护了一个 HashMap<String,ViewModel>

  1. get
 final ViewModel get(String key) {        return mMap.get(key);    }
很简略就是依据key获取ViewModel
  1. put
 final void put(String key, ViewModel viewModel) {        ViewModel oldViewModel = mMap.put(key, viewModel);//oldViewModel指的是被笼罩的ViewModel        if (oldViewModel != null) {            oldViewModel.onCleared();        }    }

将一个 ViewModel 增加到 map 中,并且将被笼罩的 ViewModel 数据清理(因为同一个 activity 中只能存在一个同类型的 ViewModel)

  1. clear

下一节会讲

clear 办法的调用机会(重点)

先看一下 clear 办法

public final void clear() {      for (ViewModel vm : mMap.values()) {          vm.clear();      }      mMap.clear();  }

clear 办法的调用地位在 ComponentActivity 的构造方法中,代码如下:

getLifecycle().addObserver(new LifecycleEventObserver() {            @Override            public void onStateChanged(@NonNull LifecycleOwner source,                    @NonNull Lifecycle.Event event) {                if (event == Lifecycle.Event.ON_DESTROY) {                    // Clear out the available context                    mContextAwareHelper.clearAvailableContext();                    // And clear the ViewModelStore                    if (!isChangingConfigurations()) {//当有配置信息存在的时候返回:true(屏幕旋转,后盾被杀死),当没有配置信息的时候返回:false(利用被被动杀死)。                        getViewModelStore().clear();                    }                }                }            });

豁然开朗,原来 ViewModel 通过 LifeCycle 察看 Activity 生命周期的形式来解决数据的清理操作

关注我的公众号 “安安安安卓” 收费学常识粗体