jetpack之viewModel理解

引言

最近重新学习了Jetpacl相关组件,在这里记录下

先提出几个问题

ViewModel是什么?它有什么作用
屏幕旋转或者配置变更时,如何进行Activity数据保存
ViewModel是如何做到数据保存的

ViewModel介绍

ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据,ViewModel中数据会一直存活即使 activity configuration发生变化,比如横竖屏切换的时候

viewModel的简单使用
  1. 加入依赖

implementation ‘androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0’

  1. 创建一个类继承ViewModel,里面放入项目需求的数据。这里可以选择继承viewModel或者继承AndroidViewModel,区别是AndroidViewModel带有application的环境
class PictureInfoViewModel : ViewModel() {

    lateinit var uriList: ArrayList<Uri>
    var currentPicture: MutableLiveData<Int> = MutableLiveData()
}

class PictureInfoViewModel(application: Application) : AndroidViewModel(application) {

    lateinit var uriList: ArrayList<Uri>
    var currentPicture: MutableLiveData<Int> = MutableLiveData()

}
  1. 在Activity中创建viewModel,在老版本是使用ViewModelProviders类,因为扩展性不好,该类被废弃,推荐使用ViewModelProvider进行构建,看上去只是多了一个s,相比之前,我们需要多传入一个实现Factory类create方法的参数,看起来要多传参数变得麻烦了,但是我们可以创建Factory实现类定制化create方法构建viewModel,如果你不想那么麻烦去重写方法,可以直接使用系统的NewInstanceFactory或者是AndroidViewModelFactory来获得一个工厂对象,这两个区别就是AndroidViewModelFactory需要传入application,刚好是给上一步的AndroidViewModel来使用。下面是几中常见的创建方式
// 方式一 : 使用by关键字用委托的方式构建,使用ViewModelProvider类进行,
    private val viewModel: PictureInfoViewModel by lazy {
        ViewModelProvider(
            this,
            ViewModelProvider.NewInstanceFactory()			// 这里可以用系统的也可以自己实现Factory
        ).get(PictureInfoViewModel::class.java)
    }

// 方式二: 用ComponentActivity提供的扩展函数来构建,如果对Factory没有特别需求,推荐使用这种方式,既简单又方便
private val viewModel: PictureInfoViewModel by viewModels()

// 扩展函数的实现,传进来的Factory为不为空,为空就走默认流程了
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
  • 先看一张官方的viewModel生命周期图
  • 可以看到只有在Activity被销毁时机才会销毁viewModel,具体是怎么触发销毁的我们后面继续分析源码
源码分析
创建方法分析

上面讲到两种构建viewModel的方式,其实两种方式都一样,写法上的区别而已,最终都会调到ViewModelProvider(store, factory).get(viewModelClass.java)

  • 这里就是一个viewModel仓库和一个用于构建ViewModel的工厂
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }


// ViewModelStore其实就是一个hashMap,缓存ViewModel
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

  • 大致流程就是先从viewModel仓库里面通过Key取拿ViewModel,拿不到就通过我们get传入的class类进行反射创建出ViewModel来
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) 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);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
  • 回到我们创建时,为什么传入this就能得到viewModelStore?往源码里找,原来是Activity的爷爷类ComponentActivity,实现了ViewModelStoreOwner接口
public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}

	// ComponentAcitivty中的具体实现
    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;
    }

	// 往下继续看,如果为空,会尝试从getLastNonConfigurationInstance()里面拿到nc对象
	// 再从nc里面获得viweModelstore,getLastNonConfigurationInstance是啥我们后面分析
    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();
            }
        }
    }
  • 创建看完了,我们再来看看如何销毁的,在ComponentActivity初始化时,使用lifecycle添加了监听,我们都知道,lifecycle生命周期发生改变的时候就会回调到这个onStateChanged方法,当回调的事件为destory也就是activity调用了onDestroy的时候,会进入下面的判断,走到getViewModelStore().clear(),清空viewModel
 public 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()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
       ..............
    }

总结下:viewModel使用Factory进行创建,并把自身缓存在acitivty的viewModelStore中,这是一个haspMap数据结果,里面能放很多ViewModel,当acitivty销毁时,通过lifecycle的监听回调销毁viewModel

Activity状态变更的数据恢复

先来复习下界面旋转时走的生命周期
onPause()->onStop()->onSaveInstanceState()->onDestroy()->onCreate()->onStart()->onRestoreInstanceState()->
onResume()
在这里插入图片描述

  • 由此可以得到数据恢复的方法一:重写onSaveInstanceState存储那些我们想要保留的重要的临时数据,把他存放在Bundle对象里面,下次再重新开启这个Activity的时候,会执行oncreate方法,我们就可以重onCreate方法的参数中接受这个存储数据的Bundle对象,从里面取出数据。或者从onRestoreInstanceState中取出数据也是同理

  • 方式二 : 通过Activity提供的两个方法,onRetainNonConfigurationInstance进行保存数据,然后通过getLastNonConfigurationInstance恢复保存数据。细心的同学应该发现了,ViewModel也就是通过getLastNonConfigurationInstance获得上一次保存的数据,google已经不推荐我们进行重写,因为已经有ViewModel了嘛

  • 具体ViewModel实现数据保存和恢复的代码

		// 数据保存
	    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;
    }


	// 数据恢复,这里就是我们上面分析创建流程时的代码,通过getLastNonConfigurationInstance获得上一次的viewModel数据
    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();
            }
        }
    }
总结:ViewModel为什么在旋转屏幕后不会丢失状态?
  • 通过两个方法getLastNonConfigurationInstance和onRetainNonConfigurationInstance,当设备的配置发生变化时(例如屏幕翻转),AMS通过binder回调到onRetainNonConfigurationInstance保存NonConfigurationInstances的一个实例对象,里面存放着viewModelStore保存activity的所有viewModel信息,当activity重建完成,创建viewModel时,会调用到getLastNonConfigurationInstance()从nc里面找到viewModelStore,然后取出所需要的viewModel,完成数据的恢复
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值