jetpack之ViewModel
Posted 做一个苦行僧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jetpack之ViewModel相关的知识,希望对你有一定的参考价值。
ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。
摘自官方文档
android 框架可以管理界面控制器(如 Activity 和 Fragment)的生命周期。Android 框架可能会决定销毁或重新创建界面控制器,以响应完全不受您控制的某些用户操作或设备事件。
如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。因配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState()方法中保存从 onCreate()中恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
另一个问题是,界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。 Activity 和 Fragment 太多的逻辑控制代码降低了代码阅读性和可维护性。
从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。
ViewModel 的生命周期
ViewModel对象存在的时间范围是获取 ViewModel时传递给 ViewModelProvider的Lifecycle。ViewModel将一直留在内存中,直到限定其存在时间范围的 Lifecycle永久消失:对于 activity,是在 activity finished的时候;而对于 fragment,是在 fragment detach时候
我们通常在Activity 的onCreate()方法中创建ViewModel(避免重复创建).系统可能会在Activity的整个生命周期内多次调用onCreate()方法,例如旋转手机屏幕的时候。ViewModel存在的时间范围是从首次创建ViewModel直到activity finished 并销毁
ViewModel的创建
ViewModel使用很简单,我们只需要继承ViewModel类就行了
class ViewModelActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
val binding = ActivityViewModelBinding.inflate(layoutInflater)
setContentView(binding.root)
val model = ViewModelProvider(this).get(MyViewModel::class.java)
model.name.observe(this)
//update UI 的操作
在Activity中,我们可以使用ViewModelProvider来得到 ViewMode的实例。
如果在ViewModel中,我们需要使用到上下文Context对象(toast 或者获取系统服务等等),我们可以继承AndroidViewModel来构建ViewModel
class AndroidModel(app:Application): AndroidViewModel(app)
此时这个AndroidModel的创建和上面的类似,也是用ViewModelProvider来获得。
ViewModel常常结合LiveData使用,然后在我们的Activity或者Fragemnt中去监听LiveData的改变
然后去在Activity中做UI的更新逻辑,例如,我们需要根据网络请求来决定是否弹出一个DialogFragment,我们的网络请求放在ViewModel中,DialogFragment必须在Activity或者Fragment中弹出来(因为DialogFragment的弹出不能使用Application作为Context),所以此时我们必须使用LiveData。当网络请求完成之后,我们改变LiveData的值,并且在Activity或者Fragment监听LiveData的变化,然后作出弹出DailogFragment的操作
在Fragment之间共享数据
现在的App中使用Fragment是很常见的,之前我们从Activity中向Fragment中传递数据,我们使用Bundle来传递(在创建Fragment的时候),但是在Activityt中如果需要动态传递(随时传递)数据给Fragment,我们平常的做法可能是在Activity中持有Fragment的应用,然后在Activity中去调用对于Fragment的某些方法传递数据,或者利用通知系统(EventBus),但是我们如果在BFragment中促使Activity中的数据改变,要通知到CFragment的Ui修改的话,目前的场景只能使用EventBus。当然也可以使用我们这里的ViewModel了
class ShareViewModel : ViewModel()
val selected = MutableLiveData<Item>()
fun select(item: Item)
selected.value = item
class ListFragment : Fragment()
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: ShareViewModel?=null
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
itemSelector.setOnClickListener item ->
// Update the UI
class DetailFragment : Fragment()
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: ShareViewModel?=null
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
model.selected.observe(viewLifecycleOwner, Observer<Item> item ->
// Update the UI
)
在上面的两个Fragment 在获取ViewModel的时候 传递的都是requireActivity(),那么获取到的ViewModel的实例其实就是同一个SharedViewModel,所以当Activity 或者任何一个Fragment中 改变了SharedViewModel中LiveData的数据,都会及时的通知到。这种方法有以下好处:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了 SharedViewModel之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
- 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
ViewModel的源码解析
ViewModel的代码很简单
这里基本只需要知道onCleared()方法就行了,自定义ViewModel并重写这个方法,讲释放资源的逻辑放在这个方法中就行
ViewModelProvider类
获取ViewModel的实例时,我们是使用ViewModelProvider来获取的
public ViewModelProvider(@NonNull ViewModelStoreOwner owner)
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory)
this(owner.getViewModelStore(), factory);
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory)
mFactory = factory;
mViewModelStore = store;
创建ViewModelProvider的时候需要传递一个ViewModelStore 和一个Factory,而我们构建的时候只传递了一个this(Activity),其实就是一个ViewModelStoreOwner,
AppCompatActivity -->FragmentActivity -->ComponentActivity --> ViewModelStoreOwner
有上面这样一个继承实现关系,我们的AppCompatActivity其实可以说是实现了ViewModelStoreOwner的,最终返回的是ComponentActivity中的mViewModelStore 关于Factory后面再讲
@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的方法,可以看到在ensureViewModelStore方法中,我们会首先判断mViewModelStore 是否为null,然后通过getLastNonConfigurationInstance() 得到一个NonConfigurationInstances 实例,这里其实就是当Activity旋转的时候ViewModel中的数据还会存在的奥秘,通过nc 可以获取重建之前的mViewModelStore,然后从ViewModelStore里面根据类名获取ViewModel的实例,所以获取到的ViewModel在旋转前后其实是同一个实例,在我们的App系统configuration发生改变的时候 就会回调onRetainNonConfigurationInstance()这个方法
@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;
//Activity中 的方法
@Nullable
public Object getLastNonConfigurationInstance()
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
这里就是将ViewModelStore进行保存。 这里getLastNonConfigurationInstance 方法 最后其实是返回Activity中的mLastNonConfigurationInstances 变量的activity对象,我们看看这mLastNonConfigurationInstances 在哪里赋值,我们知道,我们的Activity的启动其实最终都会走到ActivityThread类中,当我们启动一个Activity的时候会执行其中的 performLaunchActivity 方法最终会调用到Activity的attach方法,lastNonConfigurationInstances是存在 ActivityClientRecord中的一个组件信息
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason)
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null)
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing)
r.activity.mFinished = true;
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped)
callActivityOnStop(r, false /* saveState */, "destroy");
if (getNonConfigInstance)
try
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
catch (Exception e)
if (!mInstrumentation.onException(r.activity, e))
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
try
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
if (!r.activity.mCalled)
throw new SuperNotCalledException(
"Activity " + safeToComponentShortString(r.intent) +
" did not call through to super.onDestroy()");
if (r.window != null)
r.window.closeAllPanels();
catch (SuperNotCalledException e)
throw e;
catch (Exception e)
if (!mInstrumentation.onException(r.activity, e))
throw new RuntimeException(
"Unable to destroy activity " + safeToComponentShortString(r.intent)
+ ": " + e.toString(), e);
r.setState(ON_DESTROY);
schedulePurgeIdler();
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager)
mActivities.remove(token);
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
在屏幕旋转造成的的Activity重建的时候 就会给lastNonConfigurationInstances 这个变量赋值,这样就能够在Activity重建的时候 获取到之前的ViewModel了。而我们的ViewModel中onClear方法什么时候执行呢,在我们的ComponetActivity构方法中
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();
);
上面注册了一个Lifecycle的监听,在我们的Activity在onDestory之后 并且isChangingConfigurations()为false的时候,才会去执行getViewModelStore().clear(); 的操作间接调用到ViewModel的onCleared()方法
get()获取ViewModel
在这里通过get方法创建ViewModel 传入的参数是 所需要创建的ViewModel的Class对象
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);
@SuppressWarnings("unchecked")
@NonNull
@MainThread
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;
最终通过下面的get方法获取ViewModel,当我们从ViewModelStore 根据key值去获取ViewModel为null的时候,如果为null 就是用Factory进行创建。所以后面我们也可以定义自己的Factory 来创建ViewModel
ViewModelStore类
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();
这里其实就是一个 HashMap存放了ViewModel,主要是ViewModel的存取
以上是关于jetpack之ViewModel的主要内容,如果未能解决你的问题,请参考以下文章
搞懂Android Jetpack ViewModel 使用及原理