在早期的Android开发中,因为应用相对较小,页面相对简单,咱们会将数据请求、页面UI处理和数据加载所有放在Activity或Fragment中进行,可是随着项目的迭代,这种开发方式显得愈来愈臃肿,而且也不易于项目的维护和扩展。android
此时,借鉴后端的后端程序的开发思路,咱们对Android项目进行了分层,典型的有MVC,MVP和MVVM等项目分层,而后每层负责本身的事情便可。以如今流行的MVVM模式为例。数据库
在JetPack架构中,ViewModel组件是一个能够感知生命周期的形式来存储和管理视图相关的数据的组件,所以它适合如下场景。后端
除此以外,ViewModel的好处还有不少,可是最终的目的就是为了让代码可维护性更高,下降代码的冗余程度。网络
咱们知道,Android的Activity/Fragment是有生命周期的,咱们能够在不一样的生命周期函数中执行不一样的操做来达到不一样的目的。因为ViewModel是保存在内存中的,因此ViewModel的生命周期并不会随Activity/Fragment的生命周期发生变化 。架构
下图是官方给出的ViewModel与Activity的生命周期的对应关系示意图。
从上图能够看出,ViewModel会伴随着Activity/Fragment的整个生命周期,直到ViewModel绑定的Activity/Fragment执行onDestroy()方法以后才会被销毁。app
1,添加gradle以来,以下所示。ide
dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0' }
2,建立一个继承自ViewModel类的MyViewModel类,建立ViewModel类千万不能持有Context的引用,不然会引发内存泄漏,若是须要使用Context能够继承AndroidViewModel。函数
public class MyViewModel extends ViewModel { private MutableLiveData<String> user; public LiveData<String> getUsers() { if (user == null) { user = new MutableLiveData<String>(); loadUsers(); } return user; } private void loadUsers() { user.setValue("Android应用开发实战"); } }
3, 为了不内存泄漏,咱们能够在onCleared()方法中进行资源释放操做。而后,咱们在Activity中就可使用MyViewModel,以下所示。源码分析
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class); model.getUsers().observe(this, new Observer<String>() { @Override public void onChanged(@Nullable String s) { Log.d(TAG, "LiveData监听数据返回:"+s); } }); } }
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 onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); model.getSelected().observe(getViewLifecycleOwner(), { item -> // Update the UI. }); } }
ViewModel类是一个抽象接口,其部分源码以下。gradle
public abstract class ViewModel { @Nullable private final Map<String, Object> mBagOfTags = new HashMap<>(); private volatile boolean mCleared = false; @SuppressWarnings("WeakerAccess") protected void onCleared() { } @MainThread final void clear() { mCleared = true; if (mBagOfTags != null) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) { // see comment for the similar call in setTagIfAbsent closeWithRuntimeException(value); } } } onCleared(); } @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) { closeWithRuntimeException(result); } return result; } @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"}) <T> T getTag(String key) { if (mBagOfTags == null) { return null; } synchronized (mBagOfTags) { return (T) mBagOfTags.get(key); } } private static void closeWithRuntimeException(Object obj) { if (obj instanceof Closeable) { try { ((Closeable) obj).close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
能够发现,ViewModel抽象类的主要做用就是使用HashMap存储数据。ViewModel 有一个子类AndroidViewModel,它的源码以下。
public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"}) @NonNull public <T extends Application> T getApplication() { return (T) mApplication; } }
与继承ViewModel不一样,AndroidViewModel须要提供一个 Application 的 Context。
在前面的示例代码中,咱们在Activity中使用ViewModelProviders.of方法来获取ViewModel实例,以下所示。
MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
打开ViewModelProviders类的源码,能够发现ViewModelProviders一共有四个构造方法,都是用来建立ViewModelProvider对象,只不过参数不一样而已。
public static ViewModelProvider of(@NonNull Fragment fragment) { return of(fragment, null); } public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } 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); } 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); }
在构建ViewModelProvider的时候须要用到ViewModelStore和Factory,下面咱们来分别介绍一下它们。
ViewModelStore主要做用是存储ViewModel的容器,当咱们打开ViewModelStore的源码时会发现ViewModelStore是经过HashMap来存储ViewModel的数据的。而且,ViewModelStore还提供了一个clear方法,用来清空Map集合里面的ViewModel,咱们能够在Activity/Fragment的onDestroy方法执行clear方法执行ViewModel数据的清除。
protected void onDestroy() { super.onDestroy(); if (mViewModelStore != null && !isChangingConfigurations()) { mViewModelStore.clear(); } }
当咱们使用ViewModelProvider获取ViewModel实例时,ViewModelProvider一共提供了4个构造函数,另外一个比较重要的构造函数是
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory{ mFactory = factory; mViewModelStore = store; }
ViewModelProvider的第二个参数是factory,它的子类有NewInstanceFactory和AndroidViewModelFactory两个,咱们可使用ViewModelProvider.AndroidViewModelFactory.getInstance
获取单例的Factory对象,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; @NonNull public static AndroidViewModelFactory getInstance(@NonNull Application application) { if (sInstance == null) { sInstance = new AndroidViewModelFactory(application); } return sInstance; } private Application mApplication; 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); } }
AndroidViewModelFactory实例化构造方法里面有个参数class,能够引用Context,实际上是Appplication实例。在上面的代码中,若是是有application参数,则经过newInstance(application)实例化,不然调用父类的create方法而后经过newInstance()实例化。若是经过newInstance(application)实例化,就能够在ViewModel里面拿到Context,因为Application是APP全局的生命周期最长,因此就不存在内存泄露问题。
前面说过,ViewModel是不会随着Activity/Fragment的销毁而销毁的,由于ViewModel是将数据使用ViewModelStore 保存在HashMap 中,因此只要ViewModelStore不被销毁,则ViewModel的数据就不会被销毁。
众所周知,Android在横竖屏切换时会触发onSaveInstanceState,而后在还原时则会触发onRestoreInstanceState。除此以外,Android的Activity类还提供了onRetainNonConfigurationInstance和getLastNonConfigurationInstance两个方法,当设备状态发生改变时,上面的两个方法就会被系统调用。
其中,onRetainNonConfigurationInstance 方法用于处理配置发生改变时数据的保存,而getLastNonConfigurationInstance则用于恢复建立Activity时获取上次保存的数据。首先,咱们来看一下onRetainNonConfigurationInstance方法,以下所示。
public final Object onRetainNonConfigurationInstance() { Object custom = onRetainCustomNonConfigurationInstance(); FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig(); ... if (fragments == null && mViewModelStore == null && custom == null) { return null; } NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = mViewModelStore; nci.fragments = fragments; return nci; }
能够发现,ViewModel会将数据存储在 NonConfigurationInstances 对象中,而NonConfigurationInstances是定义在Activity里面的一个类,以下所示。
static final class NonConfigurationInstances { Object activity; HashMap<String, Object> children; FragmentManagerNonConfig fragments; ArrayMap<String, LoaderManager> loaders; VoiceInteractor voiceInteractor; }
再来看一下getLastCustomNonConfigurationInstance方法,
getLastNonConfigurationInstance方法返回的数据就是NonConfigurationInstances.activity属性,以下所示。
@Nullable public Object getLastNonConfigurationInstance() { return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null; }
如今,咱们再看一下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."); } 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; }
能够发现,在getViewModelStore方法中咱们首先会获取NonConfigurationInstances对象,不为空则从其身上拿到ViewModelStore,也就是以前保存的ViewModelStore,而后当Activity被再次建立的时候恢复数据。
须要说明的是,onRetainNonConfigurationInstance方法会在onSaveInstanceState方法以后被调用,即调用顺序一样在onStop方法和 onDestroy方法之间。