Jetpack - ViewModel 入门与源码分析

376 阅读5分钟

介绍

ViewModel 是介于 View(视图)和 Model之间的一个东西。它起到了桥梁的作用,使视图和数据既能够分离开,也能够保持通信。

ViewModel 将页面所需要的数据从页面中剥离出来,页面只需要处理用户交互和展示数据。

ViewModel 一般是通过 LiveData 或 DataBinding 两种方式来通知页面数据发生变化,更新 UI

ViewModel的生命周期

ViewModel 总是随着 Activity/Fragment 的创建而创建,随着 Activity/Fragment 的销毁而销毁

image.png

通过这张ViewModel生命周期可以看出,当屏幕发生旋转而导致Activity被销毁并重新创建时,ViewModel并没有被销毁,从而帮助我们在Activity重新创建后获取数据更新UI。它和onSaveInstanceState方法相比更有优势,因为onSaveInstanceState方法只不适合大量数据的恢复操作,只能恢复少量并且被序列化和反序列化的数据,而ViewModel不仅支持大量数据,还不需要序列化、反序列化操作。

基本用法

添加依赖:

image.png

创建ViewModel类
class LiveDataViewModel:ViewModel() {
    
    private var timer: Timer? = null

    lateinit var mListener: OnTimeChangeListener
    
    var currentSecond = 0

    fun startTiming(){
        if(timer==null){
            timer = Timer()
            timer?.schedule(object :TimerTask(){
                override fun run() {
                   currentSecond ++
                    mListener.onChanged(currentSecond)
                }
            },0,1000)
        }
    }
    fun setTimeListener(listener: OnTimeChangeListener){
        this.mListener = listener
    }
    
    override fun onCleared() {
        super.onCleared()
        timer?.cancel()
    }
    interface OnTimeChangeListener{
        fun onChanged(second:Int)
    }


}
在activity中创建viewmodel并调用里面的函数用来更新数据
class MainActivity : AppCompatActivity() {

    var binding: ActivityMainBinding ? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
        val bean = User("arrom", 18);
        binding?.user = bean;
        val model = ViewModelProvider(this)[LiveDataViewModel::class.java]
        model.setTimeListener(object :LiveDataViewModel.OnTimeChangeListener{
            override fun onChanged(second: Int) {
                Log.d("MainActivity","onChanged${second}")
                bean.name = "arrom-${second}"
                bean.notifyChange()
            }
        })
        binding?.clickBtn?.setOnClickListener {
            model.startTiming()
            Log.d("MainActivity","按钮点击事件")
        }
    }
}

源码分析

val model = ViewModelProvider(this)[LiveDataViewModel::class.java]

进入ViewModelProvider类

public constructor(
    owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))
 */
public open class ViewModelProvider(
    private val store: ViewModelStore,
    private val factory: Factory
) 

创建ViewModelProvider时必须传入2个参数:

  • ViewModelStore 用来存储和管理ViewModel

  • factory 用来对viewModel进行实例话

ViewModelProvider提供了3中实例化的方法:

  1. 简单工厂模式构造
  2. 通过反射构造实例
  3. 自己实现factory构造实例
NewInstanceFactory构造实例
@Suppress("SingletonConstructor")
public open class NewInstanceFactory : Factory {
    @Suppress("DocumentExceptions")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return try {
            //返回实例对象
            modelClass.newInstance()
        } catch (e: InstantiationException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        } catch (e: IllegalAccessException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        }
    }

    public companion object {
        private var sInstance: NewInstanceFactory? = null

        @JvmStatic
        public val instance: NewInstanceFactory
            @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
            get() {
                if (sInstance == null) {
                    sInstance = NewInstanceFactory()
                }
                return sInstance!!
            }
    }
}

当我们使用NewInstanceFactory()构造实例时,查看源码可看出,它实现了Factory 接口,并且在其内部的create方法中会调用我们在之前get方法中传入的viewmodel的newInstance方法

AndroidViewModelFactory构造实例
public open class AndroidViewModelFactory(
    private val application: Application
) : NewInstanceFactory() {
    @Suppress("DocumentExceptions")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
            try {
               //通过反射获取实例对象 modelClass.getConstructor(Application::class.java).newInstance(application)
            } catch (e: NoSuchMethodException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: IllegalAccessException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: InstantiationException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: InvocationTargetException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            }
        } else super.create(modelClass)
    }

    public companion object {
        internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
            if (owner is HasDefaultViewModelProviderFactory)
                owner.defaultViewModelProviderFactory else instance

        internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"

        private var sInstance: AndroidViewModelFactory? = null
        @JvmStatic
        public fun getInstance(application: Application): AndroidViewModelFactory {
            if (sInstance == null) {
                sInstance = AndroidViewModelFactory(application)
            }
            return sInstance!!
        }
    }
}
自己实现factory构造实例
public interface Factory {
   
    public fun <T : ViewModel> create(modelClass: Class<T>): T
}

public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
    var viewModel = store[key]
    
    if (modelClass.isInstance(viewModel)) {
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    } else {
        @Suppress("ControlFlowWithEmptyBody")
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    viewModel = if (factory is KeyedFactory) {
        factory.create(key, modelClass)
    } else {
        factory.create(modelClass)
    }
    store.put(key, viewModel)
    return viewModel
}

我们在构造ViewModelProvider时,会将我们传入的这2个参数进行保存,当我们调用get方法时,该方法内首先会从ViewModelStore中通过key去获取ViewModel ,这个key就是上面的DEFAULT_KEY +类名。如果找到则直接返回,否则调用factory的create方法创建ViewModel实例放入ViewModelStore进行管理和保存并返回该实例

进入ViewModelStore类

public class ViewModelStore {

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

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

通过hashmap来对viewmodel进行管理

ViewModelStoreOwner 分析

在获取ViewModelStore的时候是通过一个ViewModelStoreOwner的接口来获取的

我们创建ViewModelProvider的时候传入的是fragment或者是activity,而fragment其实最终也是调用的activity的getViewModelStore方法

ComponentActivity类中

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.");
    }
    //如果当前组件的mViewModelStore 为空,则从NonConfigurationInstances 中去获取
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        //如果在NonConfigurationInstances 中没有找到,则创建一个新的ViewModelStore并保存起来
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

通过getViewModelStore去获取一个ViewModelStore ,当ViewModelStore 获取为空时,会调用getLastNonConfigurationInstance方法去恢复状态改变前的NonConfigurationInstances,和这个方法对应的是onRetainNonConfigurationInstance,该方法会在组件状态改变时去保存数据信息,由系统调用。

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

viewModelStore的赋值的时机

public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

在组件的状态发生改变时,会去尝试获取viewModelStore,并将viewModelStore保存在NonConfigurationInstances 中并返回。

在onRetainNonConfigurationInstance中会对viewModelStore进行保存,在getLastNonConfigurationInstance中会对viewModelStore进行恢复

小结

当我们在activity中去实例化ViewModelProvider时,所依赖的activity在其内部会调用getViewModelStore方法去获取一个ViewModelStore,如果没有则创建一个,并保存起来,然后调用ViewModelProvider.get方法将我们的viewmodel实例通过ViewModelStore进行保存管理,当我们的activity的状态发生改变,如旋转屏幕,此时系统会调用onRetainNonConfigurationInstance方法,在这个方法内会将我们的ViewModelStore进行保存。