ViewModel 在Activity或者Fragment生命週期內只有一個的存儲數據。ViewModel 裏面的數據不會由於屏幕的旋轉或者其餘配置(好比切換到多窗口模式)而丟失。可是在正常的finish()或者按返回鍵的時候,在Activity或者Fragment走到onDestroy的時候回清除ViewModel裏面的數據,避免內存泄漏。雖然屏幕旋轉Activity也會走onDestroy,可是會判斷是不是由於屏幕旋轉而致使的。因此ViewModel是一個很合格的存儲數據的類php
ViewModel 對象存在的時間比Activity的生命週期要長,禁止在ViewModel的持有Activity、Fragment、View等對象,這樣會引發內存泄漏。若是須要在ViewModel中引用Context的話,google爲咱們提供了AndroidViewModel 類來供咱們使用。java
![](https://user-gold-cdn.xitu.io/2020/5/21/17237572af65339b?w=1018&h=1090&f=png&s=126007)Activity 中的兩個或更多 Fragment 須要相互通訊是一種很常見的狀況。Activity和Fragment的之間互相調用,下面是一個Activity和兩個Fragment之間的互相調用android
3.1.1 構建一個Avtivitygit
class ViewModelDemoActivity : AppCompatActivity() {
// 建立以Activity爲維度的ViewModel
private val viewModel: DemoViewModel by lazy { ViewModelProvider(this).get(DemoViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding = DataBindingUtil.setContentView<ActivityViewmodelDemoBinding>(
this,
R.layout.activity_viewmodel_demo
)
viewModel.dataLive.observe(this, object : Observer<String> {
override fun onChanged(s: String?) {
// 當橫豎屏變換時,會從新走這裏,畢竟是View也都從新繪製了,只不過user裏面立馬有值
tv_name.text = s
}
})
tv_name.setOnClickListener{
// activity 裏面的點擊去改變值
viewModel.dataLive.value= "Activity觸發的改變"
}
viewModel.getName()
}
companion object {
fun startMe(activity: Activity) {
activity.startActivity(Intent(activity, ViewModelDemoActivity::class.java))
}
}
}
複製代碼
class DemoViewModel : ViewModel() {
val dataLive: MutableLiveData<String> =
MutableLiveData<String>()
fun getName(){
viewModelScope.launch {
delay(1000)
dataLive.value = "王者榮耀"
}
}
}
複製代碼
兩個Fragment是經過靜態方式添加到Activity的github
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent">
<TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#ED6E6E" android:textSize="30sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
<fragment android:id="@+id/one" android:name="com.nzy.mvvmsimple.viewmodel.ShareOneFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="@+id/guidline" app:layout_constraintTop_toBottomOf="@+id/tv_name" />
<fragment android:id="@+id/two" app:layout_constraintTop_toBottomOf="@+id/tv_name" android:name="com.nzy.mvvmsimple.viewmodel.ShareTwoFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="@+id/guidline" app:layout_constraintRight_toRightOf="parent" />
<androidx.constraintlayout.widget.Guideline android:id="@+id/guidline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
複製代碼
注意: 兩個Fragment獲取ViewModel的維度,要以Activity爲維度數據庫
class ShareOneFragment : Fragment() {
// 建立以Activity爲維度的ViewModel,注意 這裏是 requireActivity() 不是 getActivity(),requireActivity()不可能爲空,getActivity()有可能爲空
private val viewModel: DemoViewModel
by lazy { ViewModelProvider(requireActivity()).get(DemoViewModel::class.java) }
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
return inflater.inflate(R.layout.fragment_share_one, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// 這裏傳入是 viewLifecycleOwner(getViewLifecycleOwner()),而不會Fragment自己
viewModel.dataLive.observe(viewLifecycleOwner, Observer {
tv_name_one.text = it
})
// 在 FragmentOne 中去改變值
tv_one.setOnClickListener {
viewModel.dataLive.value = "Fragment-One,改變的值"
}
}
companion object {
@JvmStatic
fun newInstance() =
ShareOneFragment()
}
}
複製代碼
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0
注意 對於複雜的大型數據,請使用 數據庫 或者 SharedPreferences數組
下面的Fragment 和 Activity 都是androidx 包下面的,而且 lifecycle的版本是 2.2.0緩存
ViewModel的源碼不多,基本上就是一個map 裏面用來存儲關於協程和SavedStateHandleController(暫時知道這個),onCleared() 方法是供咱們本身調用,就是當Activity或者Fragmengt的關閉的時候,須要清理一些資源,好比Rxjava的流bash
//來存儲 一些東西,當Activity 關閉的時候 會清除這裏的數據
private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
//來存儲 一些東西,當Activity 關閉的時候 本身實現去清楚一些數據,好比Rxjava中的流
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
複製代碼
private val viewModel: DemoViewModel by lazy { ViewModelProvider(this).get(DemoViewModel::class.java) }
複製代碼
看ViewModelProvicder的構造方法網絡
//建立一個ViewModelProvider , 用來建立ViewModels的,並將其保留在給定ViewModelStoreOwner的存儲區中,若是owner是 HasDefaultViewModelProviderFactory 的子類,就用HasDefaultViewModelProviderFactory的工廠,像Fragment 和 ComponentActivity 都是實現了HasDefaultViewModelProviderFactory的接口
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
複製代碼
上面默認的方法是; 用來建立ViewModels的,並將其保留在給定ViewModelStoreOwner的存儲區中,若是owner是 HasDefaultViewModelProviderFactory 的子類,就用HasDefaultViewModelProviderFactory的工廠,像Fragment 和 ComponentActivity(是FragmentActivity的爹,AppCompatActivity的爺爺) 都是實現了HasDefaultViewModelProviderFactory的接口,獲取到的Frctory是 SavedStateViewModelFactory。 不然就用NewInstanceFactory去建立ViewModel,好比須要咱們在ViewModel中傳遞參數的時候,咱們能夠寫本身的Factory繼承NewInstanceFactory,走咱們本身方法Create()
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
// 看這個activity是否已經attache到application
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 (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
複製代碼
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
requireActivity().getApplication(),
this,
getArguments());
}
return mDefaultFactory;
}
複製代碼
使用
private val viewModel:UserViewModel by lazy { ViewModelProvider(this,UserViewModelFactory(repository)).get(UserViewModel::class.java) }
複製代碼
本身定義的Factory
class UserViewModelFactory(
private val repository: UserRepository
) : ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
// 傳入一個 Repository 參數
return UserViewModel(repository) as T
}
}
複製代碼
Fragment 和 ComponentActivity(是FragmentActivity的爹,AppCompatActivity的爺爺)都實現了 ViewModelStoreOwner 這個接口,
public interface ViewModelStoreOwner {
// ViewModelStore 是用來存儲 ViewModel的實例的
@NonNull
ViewModelStore getViewModelStore();
}
複製代碼
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實例,NonConfigurationInstances整個activity都裝在裏面了,NonConfigurationInstances是跟配置改變沒有關係的一個實例。
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
//從NonConfigurationInstances 恢復 ViewModelStore
mViewModelStore = nc.viewModelStore;
}
// 若是爲null,證實歷來沒有建立過,從新new 出來
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
複製代碼
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// 若是是null,說明之前沒有調用過 getViewModelStore()方法,也就是沒有調用過ViewModelProvider(requireActivity()).get(DemoViewModel::class.java)的方法來獲取 ViewModel。因此咱們看一下最後一個的NonConfigurationInstance裏面是否存在viewModelStore
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 若果nc 不等於null,就證實之前存儲過,因此從這裏取出來
viewModelStore = nc.viewModelStore;
}
}
// custom這個返回null,子類也沒重寫他的方法,因此是必須是null
if (viewModelStore == null && custom == null) {
return null;
}
// 若是viewModelStore不是null,也就是說最後一個NonConfigurationInstance裏面有值,直接new出來NonConfigurationInstances並賦值,返回出去
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
retu
複製代碼
而後看看 NonConfigurationInstances類,是ComponentActivity的一個靜態內部類,用來存儲viewModelStore的,不管配置是否改變,這個類的實例不會改,裏面的ViewModelStore不會消失
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
複製代碼
在Activity調用onDestroy的以前會調用activity的retainNonConfigurationInstances
NonConfigurationInstances retainNonConfigurationInstances() {
....
}
複製代碼
總結一下:
public class ViewModelStore {
// 這個就是存儲ViewModel的Hashmap
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());
}
// 清除viewmodel的裏面的東西
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
複製代碼
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
// 得到 viewmodel這個類的全限定名
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
// 用一個DEFAULT_KEY常量 + 一個這個類的全限定名當作key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
複製代碼
繼續看get(String,Class) 方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 你能夠認爲mViewModelStore就是個map,上面也講了,其實它裏面就是一個map來存儲ViewModel的
// 從map裏面拿到 拿到這個 ViewModel
ViewModel viewModel = mViewModelStore.get(key);
// viewModel 這個對象是否能夠轉化成這個類,若是 viewModel 是null的話,這裏也返回false
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// mFactory 若是不是本身實現的話,那就是SavedStateViewModelFactory 實現於 KeyedFactory,因此第一次拿的時候 ,走到這裏
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 把獲取到的ViewModel緩存到HashMap中
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
複製代碼
當 ComponentActivity 的構造方法中有如下代碼,當Activity走到destroy的時候清楚ViewModel,裏面有個isChangingConfigurations方法,表示 是不是配置改變引發的(好比 Activity屏幕旋轉),若是是就不清除。
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
複製代碼