一、什麼是ViewModel
ViewModel顧名思義,是以感知生命週期的形式來存儲和管理視圖相關的數據。ViewModel主要有以下的特點:
1.當Activity被銷燬時,我們可以使用onSaveInstanceState()方法恢復其數據,這種方法僅適用於恢復少量的支持序列化、反序列化的數據,不適用於大量數據,如用戶列表或位圖。而ViewModel不僅支持大量數據,還不需要序列化、反序列化操作。
2.Activity/Fragment(視圖控制器)主要用於顯示視圖數據,如果它們也負責數據庫或者網絡加載數據等操作,那麼一旦邏輯過多,會導致視圖控制器臃腫,ViewModel可以更容易,更有效的將視圖數據相關邏輯和視圖控制器分離開來。
3.視圖控制器經常需要一些時間纔可能返回的異步調用,視圖控制器需要管理這些調用,在合適的時候清理它們,以確保它們的生命週期不會大於自身,避免內存泄漏。而ViewModel恰恰可以避免內存泄漏的發生。
二、如何使用ViewModel
- 2.1 繼承ViewMode,實現自定義ViewModel。
class MyViewModel : ViewModel() {
private var users: MutableLiveData<String>? = null
fun getUsers(): LiveData<String>? {
if (users == null) {
users = MutableLiveData<String>()
setName()
}
return users
}
fun setName() {
users?.value = "onexzgj"
}
}
getName方法中創建一個MutableLiveData,並通過MutableLiveData的setValue方法來更新數據。
- 2.2 使用ViewModel
然後就可以在Activity中使用MyViewModel了,如下所示。
/**
* ViewModel的使用
* @author onexzgj
*/
class ViewModelActivity : AppCompatActivity() {
lateinit var viewmodel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
setTitle("ViewModel的使用")
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
viewModel.getUsers()?.observe(this, Observer {
Log.d("ViewModel", it)
})
}
}
通過獲取一個MyViewModel的實例,然後配合LiveData就可以觀察Name的變化,輸出結果爲:
如果重新創建了該 Activity,它接收的 MyViewModel
實例與第一個 Activity 創建的實例相同。當所有者 Activity 完成時,框架會調用 ViewModel
對象的 onCleared()
方法,以便它可以清理資源。
三、ViewModel 的生命週期
ViewModel對象存在的時間範圍是獲取ViewModel
時傳遞給 ViewModelProvider
的 Lifecycle
示例中則爲Activity,之前創建ViewModel
的實例是通過
var viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
ViewModel
將一直留在內存中,直到限定其存在時間範圍的Lifecycle
永久消失:對於 Activity,是在 Activity 完成時;而對於 Fragment,是在 Fragment 分離時。
圖 1 說明了 Activity 經歷屏幕旋轉而後結束的過程中所處的各種生命週期狀態。該圖還在關聯的 Activity 生命週期的旁邊顯示了 ViewModel`的生命週期。此圖表說明了 Activity 的各種狀態。這些基本狀態同樣適用於 Fragment 的生命週期。
可以看到,通常在系統首次調用 Activity 對象的 onCreate()
方法時請求 ViewModel
。系統可能會在 Activity 的整個生命週期內多次調用 onCreate()
,如在旋轉設備屏幕時。ViewModel
存在的時間範圍是從您首次請求 ViewModel
直到 Activity 完成並銷燬。
所以當前Activity的生命週期不斷變化,經歷了被銷燬重新創建,而ViewModel的生命週期沒有發生變化。
四、在 Fragment 之間共享數據
Activity 中的兩個或更多 Fragment 需要相互通信是一種很常見的情況。想象一下主從 Fragment 的常見情況,假設您有一個 Fragment,在該 Fragment 中,用戶從列表中選擇一項,還有另一個 Fragment,用於顯示選定項的內容。這種情況不太容易處理,因爲這兩個 Fragment 都需要定義某種接口描述,並且所有者 Activity 必須將兩者綁定在一起。此外,這兩個 Fragment 都必須處理另一個 Fragment 尚未創建或不可見的情況。
可以使用 ViewModel
對象解決這一常見的難點。這兩個 Fragment 可以使用其 Activity 範圍共享 ViewModel
來處理此類通信,如以下示例代碼所示:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
//主Fragemnt的邏輯
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
private lateinit var model: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
private lateinit var model: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
model.selected.observe(this, Observer<Item> { item ->
// Update the UI
})
}
}
請注意,這兩個 Fragment 都會檢索包含它們的 Activity。這樣,當這兩個 Fragment 各自獲取 ViewModelProvider
時,它們會收到相同的 SharedViewModel
實例(其範圍限定爲該 Activity)。
此方法具有以下優勢:
- Activity 不需要執行任何操作,也不需要對此通信有任何瞭解。
- 除了
SharedViewModel
約定之外,Fragment 不需要相互瞭解。如果其中一個 Fragment 消失,另一個 Fragment 將繼續照常工作。 - 每個 Fragment 都有自己的生命週期,而不受另一個 Fragment 的生命週期的影響。如果一個 Fragment 替換另一個 Fragment,界面將繼續工作而沒有任何問題。
五、ViewModel原理
要講解原理,我們需要先從一個點入手,那就是第2節例子中的:
viewmodel = ViewModelProvider(this).get(MyViewModel::class.java)
因爲我們是在Activity中調用的,因此this的值爲Activity,我們還可以在Fragment中調用上面的方法,那麼this的值爲Fragment,因此ViewModelProviders(this)構造方法,我們以在Activity中調用爲例。
跟蹤到androidx.lifecycle.ViewModelProvider代碼示例如下所示:
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() //註釋1
: 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;
}
可以看到在構造函數中,相當於實例化了Factory和ViewModelStore對象,因爲ViewModelActivity
間接繼承子ComponentActivity
,由於ComponentActivity實現了HasDefaultViewModelProviderFactory
,即這裏factory
爲HasDefaultViewModelProviderFactory
,我們再查看註釋1處的getDefaultViewModelProviderFactory()
方法的實現,這裏以
跟蹤到androidx.activity.ComponentActivity中的具體代碼如下所示:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
...
{
@Override
public ViewModelStore getViewModelStore() {
...
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;
}
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
...
if (mDefaultFactory == null) {
//註釋2
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
通過查看getDefaultViewModelProviderFactory()方法,裏面間接調用了androidx.lifecycle.SavedStateViewModelFactory中SavedStateViewModelFactory(…)構造方法,接下來查看SavedStateViewModelFactory()的具體實現
@SuppressLint("LambdaLast")
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
在這裏可以看到mApplication和mFactory的初始化,而mFactory是通過ViewModelProvider.AndroidViewModelFactory.getInstance(application)的實現,可以看到具體的實現方式如下,得到了一個AndroidViewModelFactory工廠。
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
到這裏可以將ViewModelProvider(this)這部分分析完了,即初始化了一個mFactory和ViewModelStore對象,
接着回頭看ViewModelProvider.get方法的實現:
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();//1
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);//2
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);//3
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;
}
註釋1處得到類的名稱,對這個名稱進行字符串拼接,作爲註釋2處方法的參數,DEFAULT_KEY的值爲: “androidx.lifecycle.ViewModelProvider.DefaultKey”。
因此,註釋3處的key值實際上就是”androidx.lifecycle.ViewModelProvider.DefaultKey”+類名。根據這個key值從ViewModelStore獲取ViewModel(ViewModel的實現類)。如果ViewModel能轉換爲modelClass類的對象,直接返回該ViewModel。
否則會通過Factory創建一個ViewModel,並將其存儲到ViewModelStore中。這裏的Factory指的是AndroidViewModelFactory,它在ViewModelProvider創建時作爲參數傳進來。
到此爲止,我們已經知道了ViewModel的實現類是如何創建的了。
當創建完ViewModel的實現類後,在第2小節我們還會調用如下代碼。
viewmodel.getUsers()?.observe(this, Observer {
Log.d("ViewModel", it)
})
model.getName()會返回一個MutableLiveData,接着調用了MutableLiveData的observe方法,這個在Android Jetpack架構組件-LiveData使用 這篇文章中講過,就不再贅述。
項目中所有的示例代碼及Jetpack全套框架均已上傳至OnexZgj_Github