ViewModel 在Activity或者Fragment生命周期内只有一个的存储数据。ViewModel 里面的数据不会因为屏幕的旋转或者其他配置(比如切换到多窗口模式)而丢失。但是在正常的finish()或者按返回键的时候,在Activity或者Fragment走到onDestroy的时候回清除ViewModel里面的数据,避免内存泄漏。虽然屏幕旋转Activity也会走onDestroy,但是会判断是否是因为屏幕旋转而导致的。所以ViewModel是一个很合格的存储数据的类
ViewModel 对象存在的时间比Activity的生命周期要长,禁止在ViewModel的持有Activity、Fragment、View等对象,这样会引起内存泄漏。如果需要在ViewModel中引用Context的话,google为我们提供了AndroidViewModel 类来供我们使用。
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。Activity和Fragment的之间互相调用,下面是一个Activity和两个Fragment之间的互相调用
3.1.1 构建一个Avtivity
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的
<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的流
//来存储 一些东西,当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 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 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 get(@NonNull String key, @NonNull Class 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();
}
}
}
});