要想獲得食物,就必須一直尋找,只有這樣,才有機會。不要氣餒,就算找不到肥羊,至少能找到一只兔子——《狼道》
前言
一、簡介
(1)ViewModel是什么
(2)ViewModel有什么用
(3)有什么優點
二、基本使用
(1)添加依賴
(2)繼承ViewModel
(3)使用方式
三、源碼分析
四、總結
五、內容推薦
六、項目參考
前言
——這篇主要是梳理一下Jetpack架構組件之一的ViewModel,并結合樓主所學做個總結。面向那些還不認識ViewModel的同學們。看完這篇可以快速了解它,并輕松使用。也想請教前輩們指點文章中的錯誤或不足的地方。本篇只針對ViewModel,不會拓展額外的知識如MVVM,若想了解更多關于Jetpack組件知識可以看樓主寫的Jetpack專欄。
一、簡介
(1)ViewModel是什么
——ViewModel 是google推出的Jetpack架構組件之一,設計成以生命周期的方式存儲和管理UI相關的數據。
舉個列子來消化一下:
——當發生橫豎屏切換或其他意外導致Activity重啟時,里面的臨時數據將會丟失。以前可以利用onSaveInstanceState()保存簡單的數據并在onCreate()中恢復,但只適用于少量可以序列化反序列化的數據,并不能適用于任何情況。這時就可以使用ViewModel來管理這些數據。當然ViewModel并不只 只有這個作用。
那ViewModel為什么可以管理這些數據呢?
主要還是因為ViewModel的生命周期比Activtiy生命周期來的更長。如:
這就要求我們在onCreate()方法時就啟動ViewModel。
從圖中可以看出當Activity意外重啟時,ViewModel也一直存活,所以把數據存交給ViewModel管理后就不會意外丟失數據。
(2)ViewModel有什么用
- 可以存儲和管理因Activity意外重啟(如:屏幕切換)丟失的數據。
- 可以管理Acitvity中使用的異步調用,或監聽事件因Activity銷毀而沒有及時清理造成的內存泄漏。
- 可以將Activity中有關數據獲取的操作移到ViewModel里面,實現視圖與數據相互分離,更容易維護。
- 可以在Fragment之間共享數據。
(3)有什么優點
針對用法,可以得出以下優點:
- 存儲和管理數據
- 避免內存泄漏
- 解耦
- 共享數據
?二、基本使用
(1)添加依賴
可以使用androidx的appcompat,里面包含許多的依賴,包括viewodel。
implementation 'androidx.appcompat:appcompat:1.0.0'
當然也可以添加自己熟悉的版本,每個版本使用方式都有稍微差別:
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.1.0'
樓主這邊要分析的是官網上的例子,所以添加的依賴是:
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
這個依賴主要作用是在使用ViewModel的基礎上多封裝了一個ViewModelProviders類。
(2)繼承ViewModel
因為ViewModel是個抽象類,所以需要聲明一個類來繼承它。
public class MyViewModel extends ViewModel {
private String name ="張三";
private String data ="網絡數據";
//獲取用戶名字
public String getUserName(){
return name;
}
//獲取數據
public String loadData(){
//加載網絡數據的邏輯
return data;
}
}
其實這邊獲取網絡數據的時候可以配合LiveData一起使用,因為加載網絡數據都會有一點的延遲。并能立即就得到數據,所以使用liveData的話,當有數據回調的時候,可以通知UI更新。但這邊只單純介紹ViewModel,就不詳細說明了。想要了解LiveData 請看《Android Jetpack架構組件之LiveData》
(3)使用方式
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 viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
Log.e(TAG, "onCreate: "+viewModel.getUserName() );
Log.e(TAG, "onCreate: "+viewModel.loadData() );
}
}
通過ViewModelProviders獲取到MyViewModel的實例,就可以使用里面的方法了。
注意:ViewModel絕對不能引用視圖、生命周期或任何可能包含對活動上下文的引用的類。
這里就不展示太復雜的代碼了,一切從簡。主要目的是想讓大家一眼就能明白它是什么,如何實現的,用最少的時間,掌握知識。
再通過分析源碼來了解它。
三、源碼分析
通過使用步驟來分析一下源碼實現過程。
(1)ViewModelProviders.of(this).get(MyViewModel.class)
首先是調用了ViewModelProviders的of()方法:源碼如下
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
//獲取Application實例
Application application = checkApplication(activity);
/**
* 若沒有自定義Factor'y的情況下默認使用viewModel提供的AndrodiViewModelFactory
* of該方法也是androidx.lifecycle:lifecycle-extensions依賴給我們封裝好
* 若不添加上面依賴的情況下 使用的時候需要手動加入該factory
*/
if (factory == null) {
//詳細看 》2
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//詳細看 》3
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
(2)ViewModelProvider.AndroidViewModelFactory.getInstance(application)
/**
* AndroidViewModelFactory 是ViewModelProvider的一個靜態內部類
* 繼承NewInstanceFactory類并重寫了create方法
* 主要是判斷ViewModel是不是繼承與AndroidVeiwMode,
* 如果是通過傳入application實例,使繼承于AndroidViewMode的實例可以拿到application
* 區別:
* 繼承ViewModel拿不到Application實例而繼承于AndroidViewMode可以拿到Application實例
* 所以AndroidViewModelFactory作用:是封裝了一層可以得到Application實例的viewmodel
*/
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* 使用單例模式 獲取AndroidViewModelFactory實例
*/
@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 繼承了 NewInstanceFactory類 并重寫了create方法
* NewInstanceFactory 主要作用是通過create方法利用反射創建一個類的實例
*/
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);
}
}
}
(3)new ViewModelProvider(activity.getViewModelStore(), factory)
/**
* 創建了ViewModelProvider實例,這樣就可以使用ViewModelProvider里面的方法
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
(4)getViewModelStore()
/**
* 當配置發生更改時一般會造成數據丟失 而NonConfigurationInstances實例則可以在
* 配置發生變化時保存一些數據和狀態,在oncreate方法恢復使數據不會丟失。
* 當配置發生變化的時候它保存了ViewModelStore。
* 所以這里先從NonConfigurationInstance實例中獲取ViewModelStore
* 否則就新建一個ViewModelStore
*/
@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;
}
/**
* ViewModelStore 用于緩存ViewModel的一個操作類
*/
public class ViewModelStore {
//用于存儲ViewModel的集合
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();
}
}
(5)ViewModelProviders.of(this).get(MyViewModel.class)
——of方法拿到了ViewModelProvider實例就可以使用get方法,這時把自定義的ViewModel類傳進去
@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);
}
/**
* 獲取ViewModel實例
*/
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從集合中獲取ViewModel并檢測是不是已經實例化,如果存在就不要新建
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//通過of()方法得到的Factory實例,調用create方法。利用反射獲取到ViewModel的實例
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//保存viewModel實例
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
其實主要還是利用了反射得到了ViewModel的實例。我們才可以使用ViewModel里面的方法。
那么什么是反射呢? 這里簡單介紹一下:
反射機制:反射機制允許程序在執行期借助于Reflection API取得任何類的內部信息,并能直接操作任意對象的內部屬性及方法
優點:可以實現動態創建對象和編譯,體現出很大的靈活性
缺點:對性能有影響,此類操作總是慢于直接執行相同的操作
(6)最后說一下VierModel是如何銷毀的
/**
* 在FragmentActivity的onDestroy方法中調用了mViewModelStore.clear()
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
/**
* 先遍歷ViewModel實例 調用各自的clear()
* 再清除集合中的ViewModel實例
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
/**
* 清除一些標記 并調用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();
}
//通過繼承ViewModel 可以重寫該方法。 該方法會在ViewMode被銷毀前調用
protected void onCleared() {
}
四、總結
通過源碼分析ViewModel有三個重要的類:ViewModel 、ViewModelProvider 、 ViewModelStore
ViewModel :負責準備和管理數據的類,該抽象類其實是聲明一些通用方法
ViewModelProvider :ViewModel 的核心類,主要是利用反射實例化出ViewModel 對象。利用工廠模式生產出具體的ViewModel 實例。
ViewModelStore:緩存ViewModel實例的一些操作(存儲、獲取、清除)
核心原理簡單通俗描述如下:
ViewModel類存儲了Actvity的UI數據
ViewModelStore又存儲了ViewModel實例
在配置發生變化的時候在FragmentActivity.onRetainNonConfigurationInstance()方法中利用NonConfigurationInstances保存了ViewModelStore實例
并在FragmentActivtiy.oncreate()方法中恢復了ViewModelStore。也就是保存了ViewModel。所以數據才不會在配置更改時丟失
最后在FragmentActivtiy.onDestroy()方法中清除存儲在ViewModelStore中的ViewModel對象。
五、內容推薦
- 《CSDN》《簡書》
- 《Android Jetpack架構組件之LiveData》
- 《Android Jetpack架構組件之Lifecycle源碼分析》
- 《Android Jetpack架構組件之Lifecycle入門》
- 《Android ButterKnife入門到放棄》
六、項目參考
自己整理的一個工具演示項目,有興趣可以看下
若您發現文章中存在錯誤或不足的地方,希望您能指出!