JetPack之ViewModel理解分析

文章目录

      • 提出问题:为什么viewmodel能保留界面状态
        • 1.viewmodel的实现原理
        • 2. 什么是viewmodel
        • 3.viewmodel是拿来干嘛的
        • 4.viewmodel有什么优势

提出问题:为什么viewmodel能保留界面状态

1.viewmodel的实现原理

我们都知道activity在屏幕旋转的时候会重建,这会导致之前activity的数据丢失。

代码举例:
MyViewModel代码

    var index = 0
    fun add(callback:(result:Int) -> Unit){
        index++
        callback.invoke(index)
    }

Activity中代码

private val myViewModel by viewModels<MyViewModel>()
val tv = findViewById<TextView>(R.id.tv)
val bt = findViewById<Button>(R.id.bt)
bt.setOnClickListener {
  myViewModel.add {
     tv.text = "$it"
  }
}

这个时候点击操作后触发viewmodel类中add方法,然后index++,然后通过回调函数在activity中更新UI,
当Activity启动的时候TextView显示是0,然后通过点击Button按钮后执行index++操作然后TextView更新UI显示1,这个时候用户旋转屏幕之后我们发现TextView显示结果会重置为0。很多人会想是不是ViewModel没有保存index的数据呢?为了验证ViewModel到底有没有保存数据状态,我又点击Button按钮后TextView更新UI显示的结果是2。这个时候很显然可以确定ViewModel是有保存数据状态,如果你们不相信可以打印一下viewmodel中index的值。那么页面为什么没有更新UI是因为我们是通过回调函数去触发UI刷新的,所以为了能实现activity重建之后页面数据更新,谷歌就推出了LiveData。这个我们下篇文章分析LiveData的原理,这里我们主要分析ViewModel为什么在activity销毁重建之后还能保存数据。下面来看看ViewModel的源码:
首先从创建viewmodel对象方法入手:
第一步

private val myViewModel by viewModels<MyViewModel>()

第二步

@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(
        VM::class,
        { viewModelStore },
        factoryPromise,
        { extrasProducer?.invoke() ?: this.defaultViewModelCreationExtras }
    )
}

第三步

public class ViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory,
    private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty }
) : Lazy<VM> {
    private var cached: VM? = null

    override val value: VM
        get() {
            val viewModel = cached
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                ViewModelProvider(
                    store,
                    factory,
                    extrasProducer()
                ).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

    override fun isInitialized(): Boolean = cached != null
}

第四步

    @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
        val canonicalName = modelClass.canonicalName
            ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
    }

第五步

   @Suppress("UNCHECKED_CAST")
    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        val viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel!!)
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key
        // AGP has some desugaring issues associated with compileOnly dependencies so we need to
        // fall back to the other create method to keep from crashing.
        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }

从这里我们可以知道viewmodel对象会通过store[key]获取,如果获取到就返回,没有获取到就创建。
store指的是ViewModelStore,它的作用是存储所有的viewmodel实例。那么我们先看看是怎么获取ViewModelStore实例的。
在ComponentActivity中getViewModelStore方法
第六步

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }

第七步

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

这里我们可以看出获取viewmodelstore首先通过getLastNonConfigurationInstance()获取NonConfigurationInstances实例

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

NonConfigurationInstances实例有两个参数其中一个就是用来保存ViewModelStore实例的。
NonConfigurationInstances是ComponentActivity的内部类,在屏幕销毁重建的时候NonConfigurationInstances实例也会新建。所以就到下面第八步。
第八步

@Override
    @Nullable
    @SuppressWarnings("deprecation")
    public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

这里在屏幕旋转的时候会触发onRetainNonConfigurationInstance方法,然后在方法内部创建局部变量存储viewModelStore保存mViewModelStore,然后因为viewModelStore不等于null,会走到创建NonConfigurationInstances实例,然后给实例赋值。然后在获取ViewModelStore实例的时候会走第七步方法,这里我把代码往下移了方便观看,此时通过ensureViewModelStore方法里面获取getLastNonConfigurationInstance方法会获取到在onRetainNonConfigurationInstance方法创建的NonConfigurationInstances实例,然后通过mViewModelStore = nc.viewModelStore方法获取到被销毁activity的viewModelStore对象。

第七步
```kotlin
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

我这边在断点调试的时候打印的对象实例
onRetainNonConfigurationInstance方法
在这里插入图片描述
ensureViewModelStore方法JetPack之ViewModel理解分析_第1张图片
所以在屏幕旋转后Activity重建之后ViewModelStore的实力对象是一个,那自然存储在ViewModelStore里面的所有viewmodel都会保存状态。这也就是为什么ViewModel能持久保留界面状态的原因。

综上所述再来回看这几个问题:

2. 什么是viewmodel

ViewModel 类是一种业务逻辑或屏幕级状态容器。

3.viewmodel是拿来干嘛的

它用于将状态公开给界面,以及封装相关的业务逻辑

4.viewmodel有什么优势

1.持久保留界面状态
2.提供对业务逻辑的访问权限

你可能感兴趣的:(Android,android,android,jetpack)