2.1.2.Architecture components_ViewModel
Posted muouren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.1.2.Architecture components_ViewModel相关的知识,希望对你有一定的参考价值。
参考
https://developer.android.com/topic/libraries/architecture/viewmodel
官方例子
https://github.com/android/architecture-components-samples/tree/master/BasicSample
ViewModel
ViewModel类旨在以生命周期感知的方式存储和管理与UI相关的数据。
ViewModel类允许数据幸免于配置更改(例如屏幕旋转)。
ViewModel其实算是Presenter。
对于少量的数据可以在activity的onSaveInstanceState保存,在下次重建activity的onCreate中恢复,但对于大量数据(比如bitmap)是不能在onSaveInstanceState中保存的,因为会造成卡顿,甚至anr,所以可以用全局的缓存位置来保存此类数据,而ViewModel就是官方提供给我们使用的一个这样的库。
简单使用:
1. 继承ViewModel,假设是MyViewModel,在其中写入你自己的逻辑
2. 通过ViewModelProviders.of(this).get(MyViewModel.class),来获取此ViewModel的实例。
public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { // Create a ViewModel the first time the system calls an activity‘s onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class); model.getUsers().observe(this, users -> { // update UI }); } } public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<User>>(); loadUsers(); } return users; } private void loadUsers() { // Do an asynchronous operation to fetch users. } }
l 如果重新创建了Activity,则它将收到与第一次Activity创建的相同的MyViewModel实例。
l 当Activity不是由于系统原因,即用户明确finish时,系统将调用ViewModel对象的onCleared()方法,以便清理资源。这个在后边会进行一些源码分析。
l ViewModel对象可以包含LifecycleObservers,例如LiveData对象。 但是,ViewModel对象绝不能观察对生命周期感知型的可观察对象的更改,例如LiveData对象。
注意:ViewModel不要引用view,lifecycle,或者任何持有activity context的对象,否则会导致内存泄漏。
AndroidViewModel
如果ViewModel需要Application context(例如,查找系统服务),则可以扩展AndroidViewModel类。
public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } /** * Return the application. */ @SuppressWarnings("TypeParameterUnusedInFormals") @NonNull public <T extends Application> T getApplication() { //noinspection unchecked return (T) mApplication; } }
ViewModel的生命周期
l ViewModel是不能跨activity的,是和当前activity关联的,不过可以在此activity内的fragment之间共享。
l 如果想跨activity,则可以用AndroidViewModel。
ViewModel保留在内存中,直到其生命周期范围永久消失:
l 对于activity而言,它finish时,
l 对于fragment而言,当它detached时。
图1说明了activity经历屏幕旋转,重建,然后结束时的各种生命周期状态。
该图还在关联的activity生命周期旁显示了ViewModel的生命周期。 相同的基本状态适用于Fragment的生命周期。
当Activity不是由于系统原因,即用户明确finish时,系统将调用ViewModel对象的onCleared()方法,以便清理资源。这个在后边会进行一些源码分析。
在fragment之间共享数据
ViewModel是和当前activity关联的,是不能跨activity的。
一个activity中的两个或更多fragment需要相互通信是很常见的。 想象一下主从fragment的一种常见情况,您有一个fragment,用户在其中从列表中选择一个item,另一个fragment显示了所选item的内容。 处理这种情况绝非易事,因为两个fragment都需要定义一些接口描述,并且所有者activity必须将两者绑定在一起。 此外,两个fragment都必须处理另一个fragment尚未创建或不可见的情况。
可以通过使用ViewModel对象解决此常见的痛点。 这些fragment可以使用其activity范围来共享ViewModel来处理此通信,如以下示例代码所示:
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData<Item> getSelected() { return selected; } } public class MasterFragment extends Fragment { private SharedViewModel model; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); model.getSelected().observe(this, { item -> // Update the UI. }); } }
注意,两个Fragment都使通过获取activity来获得ViewModelProvider时,它们将收到相同的SharedViewModel实例,该实例的作用范围是此activity。
这种方法提供了下边的优点:
l 该activity不需要执行任何操作,也无需了解此通信。
l 除SharedViewModel外,Fragment不需要彼此了解。 如果其中一个Fragment消失了,则另一个继续照常工作。
l 每个Fragment都有自己的生命周期,并且不受另一个Fragment的生命周期影响。 如果一个Fragment替换了另一个Fragment,则UI可以继续工作而不会出现任何问题。
全局范围的ViewModel
在Application子类中创建一个ViewModelStore对象,然后用ViewModelProvider的
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) (Application需要implements ViewModelStoreOwner) 或者 public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory)
创建一个ViewModelProvider,
然后通过ViewModelProvider.get来获取对应的全局ViewModel。
用ViewModel替换loader
诸如CursorLoader之类的Loader类经常用于使应用程序UI中的数据与数据库保持同步。 您可以使用ViewModel和其他一些类来替换Loader。 使用ViewModel可将UI控制器与数据加载操作分开,这意味着类之间的强引用更少。
在使用Loader的一种常见方法中,应用程序可能使用CursorLoader来观察数据库的内容。 当数据库中的值更改时,Loader会自动触发数据的重新加载并更新UI:
ViewModel与Room和LiveData一起使用以替换Loader。 ViewModel确保数据在设备配置更改后仍然存在。 当数据库更改时,Room会通知您的LiveData,然后LiveData会使用修改后的数据更新UI。
源码分析
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
ViewModel
是一个abstract类,其中只有一个方法:
protected void onCleared() { }
当不再使用这个ViewModel并将其销毁时,将调用此方法。
当ViewModel观察到一些数据时,您需要清除这个订阅来防止这个ViewModel的泄漏,这是很有用的。
activity和fragment中会在销毁时自动调用关联的ViewModel的onCleared方法。
AndroidViewModel
继承自ViewModel,
public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } /** * Return the application. */ @SuppressWarnings("TypeParameterUnusedInFormals") @NonNull public <T extends Application> T getApplication() { //noinspection unchecked return (T) mApplication; } }
ViewModelProviders
//获取application private static Application checkApplication(Activity activity) { Application application = activity.getApplication(); if (application == null) { throw new IllegalStateException("Your activity/fragment is not yet attached to " + "Application. You can‘t request ViewModel before onCreate call."); } return application; } //获取fragment关联的activity private static Activity checkActivity(Fragment fragment) { Activity activity = fragment.getActivity(); if (activity == null) { throw new IllegalStateException("Can‘t create ViewModelProvider for detached fragment"); } return activity; } /** * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}. * <p> * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels. * * @param fragment a fragment, in whose scope ViewModels should be retained * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment) { return of(fragment, null); } /** * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity * is alive. More detailed explanation is in {@link ViewModel}. * <p> * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels. * * @param activity an activity, in whose scope ViewModels should be retained * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } /** * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}. * <p> * It uses the given {@link Factory} to instantiate new ViewModels. * * @param fragment a fragment, in whose scope ViewModels should be retained * @param factory a {@code Factory} to instantiate new ViewModels * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) { Application application = checkApplication(checkActivity(fragment)); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(fragment.getViewModelStore(), factory); } /** * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity * is alive. More detailed explanation is in {@link ViewModel}. * <p> * It uses the given {@link Factory} to instantiate new ViewModels. * * @param activity an activity, in whose scope ViewModels should be retained * @param factory a {@code Factory} to instantiate new ViewModels * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(activity.getViewModelStore(), factory); }
l 其中的Factory是用来创建ViewModel的工厂类,在ViewModelProvider中定义的。
l 可以看到如果没有传递Factory,那么就会使用ViewModelProvider.AndroidViewModelFactory。
l 可以看到ViewModelProvider在创建时除了需要传递Factory,还有一个ViewModelStore,它就是用来管理activity重建时能够获取相同实例ViewModel的,而ViewModelStore在fragment和fragmentActivity中都会创建。
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; } private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"; @NonNull @MainThread 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); } @NonNull @MainThread public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { //noinspection unchecked 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); //noinspection unchecked return (T) viewModel; }
- l public <T extends ViewModel> T get(@NonNull Class<T> modelClass)
- l public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass)
modelClass:就是ViewModel的class。
key:要获取的ViewModel实例对应的key,如果没有传递则会使用modelClass.getCanonicalName()来当key,key相同则获取的ViewModel的实例就相同。
返回一个现有的ViewModel或在范围内创建一个新的ViewModel(通常是一个Fragment或一个activity),与这个ViewModelProvider相关联。
创建的ViewModel与给定的范围相关联,并且只要该范围是活动的,它就会被保留(例如,如果它是一个activity,直到它finish或者进程被终止)。
HasDefaultViewModelProviderFactory
public interface HasDefaultViewModelProviderFactory { /** * Returns the default {@link ViewModelProvider.Factory} that should be * used when no custom {@code Factory} is provided to the * {@link ViewModelProvider} constructors. * * @return a {@code ViewModelProvider.Factory} */ @NonNull ViewModelProvider.Factory getDefaultViewModelProviderFactory(); }
Fragment和ComponentActivity都实现了此接口,
public ViewModelProvider(@NonNull ViewModelStoreOwner owner)用到了
ViewModelStoreOwner
public interface ViewModelStoreOwner { /** * Returns owned {@link ViewModelStore} * * @return a {@code ViewModelStore} */ @NonNull ViewModelStore getViewModelStore(); }
Fragment和ComponentActivity都实现了此接口,
另外在
l ViewModelProviders.of(this).get(MyViewModel.class);
l public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory)
都用到了
Factory
内部有一些Factory,就是创建ViewModel的工厂类
public interface Factory { /** * Creates a new instance of the given {@code Class}. * <p> * * @param modelClass a {@code Class} whose instance is requested * @param <T> The type parameter for the ViewModel. * @return a newly created ViewModel */ @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass); }
NewInstanceFactory
public static class NewInstanceFactory implements Factory { @SuppressWarnings("ClassNewInstance") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { //noinspection TryWithIdenticalCatches try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } }
AndroidViewModelFactory
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { private static AndroidViewModelFactory sInstance; /** * Retrieve a singleton instance of AndroidViewModelFactory. * * @param application an application to pass in {@link AndroidViewModel} * @return A valid {@link AndroidViewModelFactory} */ @NonNull public static AndroidViewModelFactory getInstance(@NonNull Application application) { if (sInstance == null) { sInstance = new AndroidViewModelFactory(application); } return sInstance; } private Application mApplication; /** * Creates a {@code AndroidViewModelFactory} * * @param application an application to pass in {@link AndroidViewModel} */ public AndroidViewModelFactory(@NonNull Application application) { mApplication = application; } @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { //noinspection TryWithIdenticalCatches try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch (NoSuchMethodException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (InvocationTargetException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } return super.create(modelClass); } }
ViewModelStore
来存储ViewModels的Class。
- l 必须通过配置更改来保留ViewModelStore的一个实例:如果这个ViewModelStore的所有者由于配置更改而被销毁并重新创建,那么所有者的新实例应该仍然拥有ViewModelStore的相同旧实例。
- l 如果这个ViewModelStore的所有者被销毁,并且不打算重新创建,那么它应该在这个ViewModelStore上调用clear(),以便通知ViewModels它们不再被使用。
- l 也就是说ViewModel能够在configuration change之间也能保留的原因就是在configuration change之间保留了ViewModelStore。
使用ViewModelStoreOwner.getViewModelStore()来检索activity和fragment的ViewModelStore。
每个FragmentActivity和Fragment都在内存创建了自己的ViewModelStore,并且在configuration change时保留。
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(); } }
以上是关于2.1.2.Architecture components_ViewModel的主要内容,如果未能解决你的问题,请参考以下文章
[Vue @Component] Simplify Vue Components with vue-class-component
[Vue @Component] Dynamic Vue.js Components with the component element