publicabstractclassViewModel { // Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460) @Nullable privatefinal Map<String, Object> mBagOfTags = new HashMap<>(); private volatile boolean mCleared = false;
/** * This method will be called when this ViewModel is no longer used and will be destroyed. * <p> * It is useful when ViewModel observes some data and you need to clear this subscription to * prevent a leak of this ViewModel. */ @SuppressWarnings("WeakerAccess") protected void onCleared() { }
@MainThread final void clear() { mCleared = true; // Since clear() is final, this method is still called on mock objects // and in those cases, mBagOfTags is null. It'll always be empty though // because setTagIfAbsent and getTag are not final so we can skip // clearing it if (mBagOfTags != null) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) { // see comment for the similar call in setTagIfAbsent closeWithRuntimeException(value); } } } onCleared(); }
/** * Sets a tag associated with this viewmodel and a key. * If the given {@code newValue} is {@link Closeable}, * it will be closed once {@link #clear()}. * <p> * If a value was already set for the given key, this call does nothing and * returns currently associated value, the given {@code newValue} would be ignored * <p> * If the ViewModel was already cleared then close() would be called on the returned object if * it implements {@link Closeable}. The same object may receive multiple close calls, so method * should be idempotent. */ @SuppressWarnings("unchecked") <T> T setTagIfAbsent(String key, T newValue) { T previous; synchronized (mBagOfTags) { previous = (T) mBagOfTags.get(key); if (previous == null) { mBagOfTags.put(key, newValue); } } T result = previous == null ? newValue : previous; if (mCleared) { // It is possible that we'll call close() multiple times on the same object, but // Closeable interface requires close method to be idempotent: // "if the stream is already closed then invoking this method has no effect." (c) closeWithRuntimeException(result); } return result; }
/** * Returns the tag associated with this viewmodel and the specified key. */ @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"}) <T> T getTag(String key) { if (mBagOfTags == null) { returnnull; } synchronized (mBagOfTags) { return (T) mBagOfTags.get(key); } }
ViewModelProvider(this, object : ViewModelProvider.Factory { overridefun<T : ViewModel>create(modelClass: Class<T>): T { return MainModel() as T } })[MainModel::class.java]
publicopenoperatorfun<T : ViewModel>get(modelClass: Class<T>): T { val canonicalName = modelClass.canonicalName ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels") returnget("$DEFAULT_KEY:$canonicalName", modelClass) }
publicopenoperatorfun<T : ViewModel>get(key: String, modelClass: Class<T>): T { var 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. } } viewModel = if (factory is KeyedFactory) { factory.create(key, modelClass) } else { factory.create(modelClass) } store.put(key, viewModel) return viewModel }
我们可以看出来好简单, get 的好简单, 如果没有传入 key, 就是用默认的, 从 store 里取而已, 没有就调用新建, 并设置进去。 不再过多详细的分析。 我们看下 store 是什么。
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."); } if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
privatefinal 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. */ publicfinal void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }