Android Jetpack之ViewModel源碼分析
ViewModel 簡介
在Android開發的時候,使用Activity、Fragment的生命周期的變動有時候是不受開發人員控制的(比如橫豎屏切換,導致Activity銷毀并重新創建),各種因素導致Android界面或被系統重新創建。當Activity需要重新創建的時候,之前與之綁定的數據也會丟失(比如EditText上輸入的數據或者網絡請求而沒有本地化存儲的數據等),導致用戶體驗極差。
但是在實際開發中,我們可以使用Android中提供的onSaveInstanceState()方法來保存臨時數據從而可以恢復用戶現場,然后重新執行OnCreate的時候,通過Bundle參數來再次獲取保存的數據。這種實現方式有較大的局限性:只能保存數據量較小的情況(比如較大的圖片就不適合),并且數據需要被序列化(Parcelable)。
ViewMode的出現解決了onSaveInstanceState的不足,ViewMode將UI控制器和數據業務進行分離,UI控制器只負責UI展示相關的工作,ViewMode用來管理和存儲與UI綁定的數據,同時ViewMode還與UI的生命周期相關聯。比如在橫豎屏切換時,與ViewMode相關聯的UI界面需要重新繪制創建時即執行onCreate方法時,就可以使用ViewMode存儲并恢復之前的用戶現場的數據,而不會因為Activity的銷毀而丟失,從而恢復用戶現場。ViewModel的還有一個好處就是可以實現Activity和Fragment之間的數據共享。
官方網址介紹:ViewModel介紹
ViewMode的使用
2.1 Gradle引入
implementation "android.arch.lifecycle:extensions:2.0.0"
2.2 創建自定義ViewModel
創建自定義ViewModel有兩種方式代碼如下。其中最大的區別是ViewMode中不能引用Activity、Fragment的實例。主要原因是:為了防止內存泄漏。如果在某種場景下需要用到上下文Context,在繼承ViewMode的時候,可以選擇繼承AndroidViewMode。由于AndroidViewMode持有的是Application,Application在單進程的應用中是單例模式,而且生命周期是最長的所以不在內存泄漏的問題。
open class MyCustomViewModel : ViewModel(){
var name: String = "demo "
}
class MyCustomAndroidViewModel(application: Application): AndroidViewModel(application){
var lastName = "demo"
}
2.3 在Activity中的使用
(1)代碼塊1 :通過ViewModelProviders的of方法綁定activity的實例進行初始化,然后通過get方法來獲取到MyViewMode的實例。
(2)代碼塊2:使用了lambda表達式,主要是對ViewModel里面的lastName變量進行重新賦值。
(3)代碼塊3:使用了Kotlin的拓展包可以不用findViewById。如果旋轉屏幕,重新執行onCreate方法,獲取到的還是之前的ViewModel實例。所以這個時候屏幕顯示的是“configChanged”文字。
class ViewModelActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
//代碼塊1
val viewModel = ViewModelProviders.of(this).get(MyCustomViewModel::class.java)
val androidViewModel = ViewModelProviders.of(this).get(MyCustomAndroidViewModel::class.java)
//代碼塊3
txt_view_model.text = androidViewModel.lastName
//代碼快2
txt_view_model.setOnClickListener { androidViewModel.lastName = "configChanged" }
}
}
open class MyCustomViewModel : ViewModel(){
var name: String = "demo "
}
class MyCustomAndroidViewModel(application: Application): AndroidViewModel(application){
var lastName = "demo"
}
2.4 ViewMode的生命周期
ViewMode在Activity被finish后才會銷毀,否則在Activity的生命周期范圍內會一直保存在內存中,或者當依附的Fragment detached后進行銷毀。
使用官網的一張圖來標注Activity的生命周期與ViewMode的生命周期關聯,如下圖所示:
源碼分析
3.1 Activity是如何實現狀態保留
在分析源碼之前我們先來學習Activity是如何實現狀態保留。Activity中的retainNonConfigurationInstances和getLastNonConfigurationInstance方法。在Android橫豎屏切換時會觸發onSaveInstanceState,而還原時會產生onRestoreInstanceState,但是Android的Activity類還有一個方法名為onRetainNonConfigurationInstance和getLastNonConfigurationInstance這兩個方法。 當發生屏幕切換時,將伴隨Destroying被系統調用。通過這個方法可以像onSaveInstanceState()的方法一樣保留變化前的Activity數據和狀態,最大的不同在于這個方法可以返回一個包含有狀態信息的Object對象,其中甚至可以包含Activity Instance本身。用這個方法保存Activity State后,通過getLastNonConfigurationInstance()在新的Activity Instance中恢復原有狀態。比如: 在恢復窗口時,我們可以不使用onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。下面的例子效果和ViewModel實現的一致的,都可以實現用戶現場的數據恢復。
(1)看一下Activity中NonConfigurationInstances的定義 。子類通過復寫onRetainNonConfigurationInstance返回的對象,其實被賦值給了NonConfigurationInstances.activity屬性。
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
(2)getLastNonConfigurationInstance方法返回的數據就是NonConfigurationInstances.activity屬性。
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
(3)下面來看一個具體的例子。繼承自Activity
- 代碼塊1復寫Activity的onRetainNonConfigurationInstance方法,給MyCustomViewModel的name賦值,并返回該對象,其實該對象被賦值給了NonConfigurationInstances.activity屬性
- 代碼塊2通過getLastNonConfigurationInstance方法獲取NonConfigurationInstances.activity屬性即MyCustomViewModel對象,然后取出name屬性賦值給TextView。
class ViewModelActivity : Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
//代碼塊2
lastNonConfigurationInstance?.let {
var data = it as MyCustomViewModel
txt_view_model.text = data.name
}
}
//代碼塊1
override fun onRetainNonConfigurationInstance(): Any {
val model = MyCustomViewModel()
model.name = "configChanged"
return model
}
}
(4)下面來看另一種具體的實例。但是是繼承自AppCompatActivity
代碼的效果和上訴一致,只不過復寫的是onRetainCustomNonConfigurationInstance方法,獲取用的是getLastCustomNonConfigurationInstance方法。
class ViewModelActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
lastCustomNonConfigurationInstance?.let {
var data = it as MyCustomViewModel
txt_view_model.text = data.name
}
Log.e("activity","class :"+this.toString())
}
override fun onRetainCustomNonConfigurationInstance(): Any {
val model = MyCustomViewModel()
model.name = "configChanged"
return model
}
}
為什么會有上面的區別呢?我們來看一下原因:
在FragmentActivity中把onRetainNonConfigurationInstance寫成final了,不允許子類復寫。但是我們在其中看到了onRetainCustomNonConfigurationInstance方法,并把onRetainCustomNonConfigurationInstance的返回值賦值給NonConfigurationInstances.custom屬性,然后返回FragmentActivity中的NonConfigurationInstances賦值給Activity中的NonConfigurationInstances.activity屬性。注意這兩個NonConfigurationInstances要區分開來。
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;
}
NonConfigurationInstances類定義。這里的NonConfigurationInstances是定義在FragmentActivity里面的一個類。
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
在來看一下getLastCustomNonConfigurationInstance方法:
(1)調用Activity中getLastNonConfigurationInstance獲取FragmentActivity中的NonConfigurationInstances對象。
(2)獲取到FragmentActivity中的NonConfigurationInstances對象后在獲取custom屬性,custom屬性就是我們復寫的onRetainCustomNonConfigurationInstance返回對象。
/**
* Return the value previously returned from
* {@link #onRetainCustomNonConfigurationInstance()}.
*/
@SuppressWarnings("deprecation")
public Object getLastCustomNonConfigurationInstance() {
NonConfigurationInstances nc = (NonConfigurationInstances)
getLastNonConfigurationInstance();
return nc != null ? nc.custom : null;
}
特別注意的點:
1. onRetainNonConfigurationInstance()在onSaveInstanceState()之后被調用。
2. 調用順序同樣在onStop() 和 onDestroy()之間。
3.2 ViewModelProviders類
- ViewModelProviders.of方法
在Activity中我們會調用如下代碼來獲取ViewModel實例
ViewModelProviders.of(this).get(MyCustomViewModel::class.java)
那么我們就看看在ViewModelProviders.of(this)都做了些什么?
在ViewModelProviders中有四個構造方法,都是創建ViewModelProvider對象,只不過參數不同而已。
(1)ViewModelProviders里面的of()函數為我們構建一個ViewModelProvider對象。而ViewModelProvider構造方法接兩個變量ViewModelStore和Factory
(2)ViewModelStore顧名思義就是存儲ViewMoel的容器
(3)在代碼塊2:如果沒有傳入Factory,則內部創建了默認的Factory,Factory是ViewModelProvider的一個內部接口,里面有一個create方法。Factory實現類是用來構建ViewModel實例的。
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) {
//代碼塊1
Application application = checkApplication(activity);
if (factory == null) {
//代碼塊2
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
在構建ViewModelProvider的時候需要用到ViewModelStore和Factory,下面我們來分別介紹一下他們。
3.3 ViewModelStore類
ViewModelStore顧名思義就是存儲ViewMoel的容器
(1)在ViewModelStore中通過HashMap存儲ViewModel
(2)在Activity和Fragment的onDestroy方法中,會執行clear方法,清空Map集合里面的ViewModel。并且會執行ViewModel的onCleared方法。
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
}
在new ViewModelProvider(activity.getViewModelStore(), factory)的時候,會通過調用androidx.fragment.app.FragmentActivity的getViewModelStore()方法獲取ViewModelStore對象。這里就用到了我們在3.1說的知識點了。
(1)如果mViewModelStore不是null,直接返回該對象
(2)如果是null,通過getLastNonConfigurationInstance獲屏幕旋轉等操作是保存的對象。注意返回的NonConfigurationInstances是FragmentActivity的。
(3)如果NonConfigurationInstances不為空,則獲取NonConfigurationInstances中的viewModelStore屬性。
(3)如果NonConfigurationInstances中的viewModelStore屬性為null,則創建一個ViewModelStore對象。
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) {
//代碼塊1
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
我們再來看看在Activity在橫豎屏切換時是如何保存viewModelStore對象的,在onRetainNonConfigurationInstance方法中將mViewModelStore賦值給了NonConfigurationInstances.viewModelStore屬性。這樣在橫豎屏切換時相當于ViewModel實例被恢復了。
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;
}
通過上面的分析,我們知道在橫豎屏旋轉的時候,其實恢復的是ViewModelStore對象,而ViewModelStore里面通過鍵值對的形式存儲著ViewModel,所以相當于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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
3.4 Factory類
在new ViewModelProvider(activity.getViewModelStore(), factory)的時候第二個參數是Factory的子類,Factory有2個實現類:是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;
/**
* 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);
}
}
(1)AndroidViewModelFactory實例化構造方法里面有參數的class,可以引用Context,其實是Appplication實例。
(2)如果是有application參數,則通過newInstance(application)實例化。否則調用父類的create方法然后通過newInstance()實例化。
(3)如果通過newInstance(application)實例化。就可以在ViewModel里面拿到Context,由于Application是APP全局的生命周期最長,所以就不存在內存泄露的問題了。
Factory類的定義非常簡單,只有一個create方法,返回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);
}
3.5 ViewModelProvider類
通過上面的步驟,我們已經構建出了ViewModelProvider對象,那么接下來就是使用它了。還記得在Activity里面的ViewModelProviders.of(this).get(MyCustomViewModel::class.java)代碼嗎?通過ViewModelProvider的get方法,就可以獲取ViewModel的實例了。而這個實例是通過上面的Factory創建的。
下面我們來看一下get方法,里面生成了一個KEY,這個KEY就是ViewModelStore的HashMap的Key了。然后調用重載的get方法 get(@NonNull String key, @NonNull Class<T> modelClass)。
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);
}
(1)根據之前的從FragmentActivity中獲取的ViewModelStore對象中獲取key對應的ViewModel。如果獲取的ViewModel是傳入的Class 的對象,則直接返回該ViewModel。
(2)通過of函數創建的Factory生產一個ViewModel實例,可能是帶有Application實例的。
(3)將Factory實例put進ViewModelStore的HashMap里面。
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.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
3.6 ViewModel類
ViewModel是一個抽象類,里面有一個onCleared方法,有一個實現類AndroidViewModel,唯一的不同點是AndroidViewModel里面有一個Application的屬性。
onCleared在Fragment和Activity的onDestory方法中通過ViewModelStore的clear調用。
public abstract class ViewModel {
/**
* 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() {
}
}
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;
}
}