MVVM-ViewModel介绍与源码解析

一 概述

ViewModel 在Activity或者Fragment生命周期内只有一个的存储数据。ViewModel 里面的数据不会由于屏幕的旋转或者其余配置(好比切换到多窗口模式)而丢失。可是在正常的finish()或者按返回键的时候,在Activity或者Fragment走到onDestroy的时候回清除ViewModel里面的数据,避免内存泄漏。虽然屏幕旋转Activity也会走onDestroy,可是会判断是不是由于屏幕旋转而致使的。因此ViewModel是一个很合格的存储数据的类php

二 ViewModel生命周期

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)

三 ViewModel的好处

  1. 不会由于屏幕旋转或者其余配置改变(好比切换到多窗口模式)而销毁,避免数据从新建立,好比网络数据从新加载的状况
  2. 当Activity或者Fragment的正常销毁的时候,自动清除数据,也就是绑定了Activity或者Fragment的生命周期,避免了内存泄漏,
  3. 多个Fragment之间或者Activity和Fragment之间更好地传递数据,进行交互
  4. 减小Activityu或者Fragment的压力,Activityu或者Fragment的只是显示数据的界面,帮助Activity和Fragment处理一些数据逻辑

3.1 因为官网的例子有点老,因此写一个共享的例子

Activity 中的两个或更多 Fragment 须要相互通讯是一种很常见的状况。Activity和Fragment的之间互相调用,下面是一个Activity和两个Fragment之间的互相调用android

具体的demo地址github

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))
        }
    }
}
复制代码

3.1.2 ViewModel的代码

class DemoViewModel : ViewModel() {
     val dataLive: MutableLiveData<String> =
         MutableLiveData<String>()


    fun getName(){
        viewModelScope.launch {
            delay(1000)
            dataLive.value = "王者荣耀"
        }
    }
}
复制代码

3.1.3 布局文件

两个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>
复制代码

3.1.4 两个 Fragment

注意: 两个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()
    }
}

复制代码

四 ViewModel 和 onSaveInstanceState 区别

4.1 ViewModel

  1. 缓存在内存中的,至关于本地缓存和网络缓存读写比较快
  2. 能够存较大的值,好比bitmap、大对象等等
  3. 数据绑定了Activity或者Fragment的生命周期,当Activity正常关闭的时候,都会清除ViewModel中的数据.好比
    • 调用finish()
    • 按返回键退出,
    • 用户直接杀进程或者是放后台内存不足,被回收了
  4. 在Activity旋转或者其余配置改变的时候,ViewModel里面是还有值得,不会销毁,
  5. ViewModel也可使用本地存储,经过SavedStateHandle实现的,使用SavedStateViewModelFactory这个工厂,这里就很少介绍了,已是正式版本了,引用是implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0

4.2 onSaveInstanceState

  1. 仅适合能够序列化再反序列化的少许数据
  2. 不适合数量可能较大的数据,如用数组或Bitmap。能够存一些简单的基本类型和简单的小对象、例如字符串,和一些id
  3. 会把数据存储到磁盘上

注意 对于复杂的大型数据,请使用 数据库 或者 SharedPreferences数组

五 ViewModel源码解析

下面的Fragment 和 Activity 都是androidx 包下面的,而且 lifecycle的版本是 2.2.0缓存

5.1 源码

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() {
    }
复制代码

5.1.1 得到ViewModelProvider

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()

5.1.1.1 ComponentActivity中获取Factory的方法

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;
    }
复制代码

5.1.1.2 Fragment中获取Factory的方法

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;
    }
复制代码

5.1.2 本身定义的Factory

使用

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
    }
}
复制代码

5.1.3 ViewModelStoreOwner: 存储ViewModel的拥有者

Fragment 和 ComponentActivity(是FragmentActivity的爹,AppCompatActivity的爷爷)都实现了 ViewModelStoreOwner 这个接口,

public interface ViewModelStoreOwner {
// ViewModelStore 是用来存储 ViewModel的实例的
    @NonNull
    ViewModelStore getViewModelStore();
}
复制代码

5.1.3.1 ComponentActivity中的得到 ViewModelStroe 的方法

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;
    }
复制代码

5.1.3.2 在看一下ComponentActivity:: onRetainNonConfigurationInstance

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;
    }
复制代码

5.1.3.3 retainNonConfigurationInstances 方法执行来存贮NonConfigurationInstances,返回结果就是NonConfigurationInstances,同时把NonConfigurationInstances存储到ActivityClientRecord中去,ActivityClientRecord是一个Activity的记录本。以后在Activity的Attach的时候,会把NonConfigurationInstances赋值给成员变量

在Activity调用onDestroy的以前会调用activity的retainNonConfigurationInstances

NonConfigurationInstances retainNonConfigurationInstances() {
     ....
 }
复制代码

总结一下:

  • 先经过NonConfigurationInstances来拿ViewModelStore,NonConfigurationInstances是一个静态内部类,不会由于配置改变(好比屏幕旋转),而从新建立
  • 若是NonConfigurationInstances没有拿到,证实这就是个新的ViewModelStore,因此直接走建立ViewModelStore流程

5.1.4 ViewModelStore 用来存储ViewModel的容器,,里面有个HashMap,用来存储ViewModel的,存储的key下面会讲到

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();
    }
}
复制代码

5.1.5 得到ViewModel

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;
    }
复制代码

5.1.6 在何时清除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();
                    }
                }
            }
        });
复制代码
相关文章
相关标签/搜索