ViewModel的使用以及源码分析

ViewModel存在意义

使用ViewModel定义的数据可以在发生屏幕旋转的时候仍然保留数据:当手机发生横竖屏旋转的时候,Activity会被重建,同时 存放在Activity中的数据 也会丢失,viewModel的生命周期和Activity不同,他可以保证手机在发生屏幕旋转的时候不会被重新创建,只有当Activity退出的时候才会跟着Activity一起销毁。

专门用于存放和界面相关的数据,只要界面可以看到的数据,都应该存放在ViewModel中,而不是Activity中

ViewModel通常持有LiveData和相关的处理逻辑,ViewModel管理的数据有一个特点,就是不会随着页面配置改变而销毁,但在页面销毁时则会正常跟着销毁。

ViewModel的生命周期

在这里插入图片描述


ViewModel的基本使用

  • 在app/build.gradle添加依赖
  • 创建自定义的ViewModel的子类,将与界面相关的数据存放在其中
  • 通过ViewModelProvider来获取ViewModel的实例,不可以直接创建ViewModel对象

以下通过实现一个计数器具体实现ViewModel的使用

(1)添加依赖

dependencies {
    ...
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
	...
}

(2)在ViewModel中存放与界面相关的数据

class MainViewModel :ViewModel() {
    var count = 0
}

(3)创建ViewModel的实例,并使用

class MainActivity : AppCompatActivity() {
    private lateinit var button :Button
    private lateinit var counter:TextView
    private lateinit var viewModel:MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button = findViewById(R.id.add)
        counter = findViewById(R.id.counter)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java) 
        //viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)   
        //方法已经被弃用了,现在使用第一种方式获取ViewModel对象
        button.setOnClickListener {
            viewModel.count++
            counter.text = viewModel.count.toString()
        }
     	counter.text = viewModel.count.toString()
     	//可以使得在旋转屏幕之后,textView展示ViewModel中的数据值,而不是每次都显示xml文件中的默认值
    }
}

向ViewModel中传递参数

  • 创建带有参构造的ViewModel
  • 自定义创建ViewModelProvider.Factory的子类
  • 根据自定义的Factory,重新获取ViewModel的实例

在上面举例的基础上,继续实现向ViewModel中传递参数

(1)自定义创建ViewModel的子类

class MainViewModel(time :Int) :ViewModel() {
    var count = time
}

(2)创建自定义的ViewModelProvider.Factory

class MainViewModelFactory(val times :Int) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(times) as T
    }
}

(3)获取ViewModel实例

class MainActivity : AppCompatActivity() {
    private lateinit var button :Button
    private lateinit var counter:TextView
    private lateinit var viewModel:MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button = findViewById(R.id.add)
        counter = findViewById(R.id.counter)
        //仅为了展示传入参数的使用,传入100并没有意义,可以通过直接改变count的初始值实现相同的效果
        viewModel = ViewModelProvider(this,MainViewModelFactory(100)).get(MainViewModel::class.java) 
        button.setOnClickListener {
            viewModel.count++
            counter.text = viewModel.count.toString()
        }
     	counter.text = viewModel.count.toString()
    }
}

ViewModel在MVVM框架中的意义

ViewModel是MVVM框架中的VM,使得数据处理和界面分开,举例说明:

class BrandModel :ViewModel() {
    private var brands : MutableLiveData<List<Brand>>? = null

    fun getBrands() :LiveData<List<Brand>>? {
        if(brands == null) {
            brands = MutableLiveData<List<Brand>>()
            loadBrands()
        }
        return brands
    }
    private fun loadBrands() {
        //进行网络请求或者一些其他的逻辑处理操作数据
    }
}

在上面定义了一个ViewModel,管理brands这组数据,并且封装了加载brands的处理逻辑。而View只需要监听brands,在回调中根据brands处理界面就好,这样就做到了界面和数据的分离。


ViewModel源码分析

  • 通过了ViewModelProvider的get方法得到了viewModel的实例
/*
创建{@code ViewModelProvider}。创建ViewModels并将它们保留在给定{@code ViewModelStoreOwner}的存储中。

此方法将使用{@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory()默认工厂}

如果所有者实现{@link HasDefaultViewModelProviderFactory}。否则将使用{@link NewInstanceFactory}。
*/

//ComponentActivity和Fragment都实现了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;
    }

通过构造方法给成员变量mFactory和mViewModelStore赋值

get方法总体:get()方法首先尝试通过mViewModelStore的get()方法获取ViewModel的实例,如果没获取到则使用mFactory的create()创建实例,创建出来后则存入到mViewModelStore中。在这里mFactory就是ViewModel的构造工厂,mViewModelStore则是ViewModel的缓存管理者。

ViewModelProvider作为ViewModel的提供者,使用缓存mViewModelStore和工厂mFactory实现,第一次提供ViewModel时会通过工厂创建出来,后续则都是从缓存中拿。


private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";
            
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);
    }
---
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
		//在mViewModelStore的map中获取
        ViewModel viewModel = mViewModelStore.get(key);
		//如果viewModel可以强转为modelClass类型
        if (modelClass.isInstance(viewModel)) {
        //如果mFactory是OnRequeryFactory类型,调用onRequery()方法,该方法当前为空实现
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
//如果mFactory是KeyedFactory类型,则调用create方法创建新的ViewModel实例,将创建好的实例放入mViewModelStore中
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
---
    static class OnRequeryFactory {
        void onRequery(@NonNull ViewModel viewModel) {
        }
    }
---
//实现KeyedFactory的类有:SavedStateViewModelFactory和AbstractSavedStateViewModelFactory
abstract static class KeyedFactory extends OnRequeryFactory implements Factory {
        public abstract <T extends ViewModel> T create(@NonNull String key,
                @NonNull Class<T> modelClass);

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            throw new UnsupportedOperationException("create(String, Class<?>) must be called on "
                    + "implementaions of KeyedFactory");
        }
    }

  • 关于ViewModelStore类的实现:ViewModelStore中使用HashMap管理ViewModel缓存,它被页面持有,并在页面真正销毁时才清空缓存。
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    //如果是新创建的ViewModel存放的时候会将原来的ViewModel中的值进行销毁,key是根据类的全限定名来定义的
    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());
    }
    
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

  • ComponentActivity实现了ViewModelStoreOwner

ViewModelStoreOwner接口getViewModelStore()的实现就是提供一个ViewModelStore实例,而ComponentActivity使用Lifecycle能力在页面销毁时调用ViewModelStore实例的clear方法,清空其中的ViewModel。

public class ComponentActivity... {

getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // Clear out the available context
                    mContextAwareHelper.clearAvailableContext();
                    //当改变不是因为页面配置引起的
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
        
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.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
        //获得activity上次销毁时保留的信息
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

获得activity上次销毁时保留的信息使用的是getLastNonConfigurationInstance()保留数据使用的是onRetainNonConfigurationInstance()

public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        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) {
            //如果保留的信息中含有viewModelStore则使用
                viewModelStore = nc.viewModelStore;
            }
        }
        //custom一直是null,如果viewModelStore为null则返回null
        if (viewModelStore == null && custom == null) {
            return null;
        }
        // 如果viewModelStore不是null,也就是说最后一个NonConfigurationInstance里面有值
        //直接new出来NonConfigurationInstances并赋值,返回出去
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

保存的数据都是NonConfigurationInstances类型的数据,并且是ComponentActivity的静态内部类,
用来存储viewModelStore的,无论配置是否改变,这个类的实例不会改,里面的ViewModelStore不会消失

static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

当执行完onRetainNonConfigurationInstance()之后会将生成的NonConfigurationInstances对象存储在LocalActivityManager中,从而将页面中每一个Activity中的数据需要进行保存的数据全部保存。之后在Activity的Attach的时候,会把NonConfigurationInstances赋值给成员变量

public HashMap<String,Object> dispatchRetainNonConfigurationInstance() {
        HashMap<String,Object> instanceMap = null;
        //mActivityArray会对每一个调用StartActivity的Activity进行保存
        final int N = mActivityArray.size();
        for (int i=0; i<N; i++) {
            LocalActivityRecord r = mActivityArray.get(i);
            if ((r != null) && (r.activity != null)) {
                Object instance = r.activity.onRetainNonConfigurationInstance();
                if (instance != null) {
                    if (instanceMap == null) {
                        instanceMap = new HashMap<String,Object>();
                    }
                    instanceMap.put(r.id, instance);
                }
            }
        }
        return instanceMap;
    }

总结:先通过NonConfigurationInstances来拿ViewModelStore,NonConfigurationInstances是一个静态内部类,不会因为配置改变(比如屏幕旋转),而重新创建
如果NonConfigurationInstances没有拿到,证明这就是个新的ViewModelStore,所以直接走创建ViewModelStore流程


前面说了ComponentActivity和Fragment都实现了HasDefaultViewModelProviderFactory接口
现在来看一下getDefaultViewModelProviderFactory()方法在ComponentActivity是如何实现的

public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        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;
    }

通过构造方法创建一个SavedStateViewModelFactory对象,传入了Application、当前ComponentActivity实例和Intent中的数据bundle。

  • SaveStateViewModelFactory
public SavedStateViewModelFactory(@Nullable Application application,
            @NonNull SavedStateRegistryOwner owner,
            @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        mFactory = application != null
                ? ViewModelProvider.AndroidViewModelFactory.getInstance(application)
                : ViewModelProvider.NewInstanceFactory.getInstance();
    }

构造方法接受的参数中,页面实例是SavedStateRegistryOwner接口类型的,通过该接口获取到SavedStateRegistry和Lifecycle。另外成员变量mFactory是AndroidViewModelFactory的单例对象。

create()方法

public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel && mApplication != null) {
        //AndroidViewModel的构造器
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
        //支持SavedState的ViewModel的构造器
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // 普通的ViewModel的创建过程
        if (constructor == null) {
            return mFactory.create(modelClass);
        }

        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel && mApplication != null) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }

create()方法支持创建三种类型的ViewModel:AndroidViewModel、支持SavedState的ViewModel、普通ViewModel,这里由于篇幅原因,只分析一下普通ViewModel的创建。普通ViewModel通过mFactory的create()方法创建出来。
根据一开始的构造方法可知:如果application != null,则创建的是AndroidViewModelFactory实例,如果为null则创建的是NewInstanceFactory实例

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
	public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
	            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
	                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);
	        }
}

如果不是AndroidViewModel类型的话,就调用父类的create方法,

public static class NewInstanceFactory implements Factory {
.....
        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);
            }
        }
    }

NewInstanceFactory的create()则是直接通过反射创建出ViewModel实例。

SaveStateViewModelFactory作为ComponentActivity和Fragment提供的对象,在NewInstanceFactory的基础上增加了对AndroidViewModel和支持SavedStated的ViewModel的创建,但对于普通的ViewModel创建,最后还是降级使用NewInstanceFactory完成。


ViewModel源码的整体思路

通过ViewModelProvider的构造方法传入ViewModelStoreOwner对象,Activity和Fragment都实现了ViewModelStoreOwner接口,根据传入的参数得到ViewModelStore和Factory,然后通过get方法得到ViewModel对象,
get方法首先会在ViewModelStore中寻找ViewModel对象如果没有就会通过Factory进行创建并且存放在ViewModelStore中。

  • ViewModelStore的获取:

通过Activity或者Fragment的getViewModelStore()方法来进行获取,首先会通过getLastNonConfigurationInstance()获取activity的保留信息,当屏幕旋转的时候会执行onRetainNonConfigurationInstance()方法,之后会将生成的NonConfigurationInstances对象存储在LocalActivityManager中,从而将页面中每一个Activity中的数据需要进行保存的数据全部保存。之后在Activity的Attach的时候,会把NonConfigurationInstances赋值给成员变量。所以当通过getLastNonConfigurationInstance()获取activity的保留信息后如果有viewModelStore就进行复用,如果没有就新建ViewModelStore

  • Factory的获取和创建ViewModel的过程

activity和Fragment都实现了HasDefaultViewModelProviderFactory接口,在ViewModelProvider的构造方法中,可以得知,通过调用getDefaultViewModelProviderFactory()方法返回的SaveStateViewModelFactory对象,通过create方法去创建ViewModel,否则就用NewInstanceFactory去创建ViewModel,比如需要我们在ViewModel中传递参数的时候,我们可以写自己的Factory继承NewInstanceFactory,走我们自己方法Create(),通过create方法去创建ViewModel,如果application != null,则通过Application的构造器创建ViewModel实例,如果为null的话,通过反射的方式创建ViewModel实例。


注意

(1)一个比较好的编程规范:给每个对应的Activty或者Fragment都创建一个对应的ViewModel,所有与界面相关的数据都应该存放在ViewModel中

(2)为什么必须要使用ViewModelProviders来获取ViewModel的实例而不是每次在OnCreate()中重新创建ViewModel创建对象?

如果在onCreate()中,则每次改变页面配置的时候还是会重新进行创建ViewModel实例,无法保存其中的数据,不能达到理想的效果。

(3)如果在Activity中创建静态变量,也可以使得屏幕旋转时的数据不会被重新创建效果和ViewModel的作用相同,为什么使用ViewModel而不是使用静态变量?

对于框架来说,没有实现像ViewModel一样将Activity的功能进行划分。
在这里插入图片描述

(4)比较onSaveInstanceState() 与 onRetainNonConfigurationInstance()

onRetainNonConfigurationInstance()这个方法可以像onSaveInstanceState()的方法一样保留变化前的Activity State,最大的不同在于这个方法可以返回一个包含有状态信息的Object,其中甚至可以包含Activity Instance本身。

(5)ViewModel 对象存在的时间比Activity的生命周期要长,禁止在ViewModel的持有Activity、Fragment、View等对象,这样会引起内存泄漏。如果需要在ViewModel中引用Context的话,google为我们提供了AndroidViewModel 类来供我们使用。

(6)ViewModel的好处:

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

(7)ViewModel 和 onSaveInstanceState 区别:

🍏ViewModel

  • 缓存在内存中的,相当于本地缓存和网络缓存读写比较快
  • 可以存较大的值,比如bitmap、大对象等等
  • 数据绑定了Activity或者Fragment的生命周期,当Activity正常关闭的时候,都会清除ViewModel中的数据.比如
    (1)调用finish()(2)按返回键退出(3)用户直接杀进程或者是放后台内存不足,被回收了
  • 在Activity旋转或者其他配置改变的时候,ViewModel里面是还有值得,不会销毁,
  • ViewModel也可以使用本地存储,通过SavedStateHandle实现的,使用SavedStateViewModelFactory这个工厂

🍎 onSaveInstanceState

  • 仅适合可以序列化再反序列化的少量数据
  • 不适合数量可能较大的数据,如用数组或Bitmap。可以存一些简单的基本类型和简单的小对象、例如字符串,和一些id
  • 会把数据存储到磁盘上
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值