目录
前言
搞清楚activityViewModels的本质
定义Application作用域的ViewMode
ApplicationViewModelLazy
BaseViewModelApplication
使用方法
自定义Application继承BaseViewModelApplication
applicationViewModels获取ViewModel实例
小结
官方文档中提到Activity 中的两个或更多 Fragment 相互通信是使用ShareViewModel,通过扩展函数activityViewModels获取同一个Activity的ViewModelStore,从而得到保存在Activity中的同一个ViewModel的实例。
具体使用如下:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData- ()
fun select(item: Item) {
selected.value = item
}
}
class ListFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer
- { item ->
// Update the UI
})
}
}
ListFragment和DetailFragment通过activityViewModels获取到的是同一个实例,因此可以互相通信。以上是两个或更多 Fragment 相互通信的方案,那如果是Activity和Fragment相互通信呢?按照这个思路我们是否可以实现一个Application级别的ViewModel?答案是肯定的!
在依葫芦画瓢之前,必须得搞清楚activityViewModels是怎么实现的,跟踪进源码,发现activityViewModels是一个扩展函数,定义在FragmentViewModelLazy.kt文件中:
/**
* Returns a property delegate to access [ViewModel] by **default** scoped to this [Fragment]:
* ```
* class MyFragment : Fragment() {
* val viewmodel: MYViewModel by viewmodels()
* }
* ```
*
* Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter,
* factory returned by it will be used to create [ViewModel]:
* ```
* class MyFragment : Fragment() {
* val viewmodel: MYViewModel by viewmodels { myFactory }
* }
* ```
*
* Default scope may be overridden with parameter [ownerProducer]:
* ```
* class MyFragment : Fragment() {
* val viewmodel: MYViewModel by viewmodels ({requireParentFragment()})
* }
* ```
*
* This property can be accessed only after this Fragment is attached i.e., after
* [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
*/
@MainThread
inline fun Fragment.viewModels(
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
/**
* Returns a property delegate to access parent activity's [ViewModel],
* if [factoryProducer] is specified then [ViewModelProvider.Factory]
* returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
* [androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory](default factory)
* will be used.
*
* ```
* class MyFragment : Fragment() {
* val viewmodel: MyViewModel by activityViewModels()
* }
* ```
*
* This property can be accessed only after this Fragment is attached i.e., after
* [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
*/
@MainThread
inline fun Fragment.activityViewModels(
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
/**
* Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
* to default factory.
*/
@MainThread
fun Fragment.createViewModelLazy(
viewModelClass: KClass,
storeProducer: () -> ViewModelStore,
factoryProducer: (() -> Factory)? = null
): Lazy {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
虽然代码量很少,但是先不管细枝末节,我们把核心骨架抽出来屡清楚思路。
可以看到activityViewModels返回的是createViewModelLazy方法创建的ViewModel实例,createViewModelLazy调用的是ViewModelLazy的实例,ViewModelLazy又是通过ViewModelProvider实例的get方法获取到ViewModel的实例。
图1
图1已经比较完整的把ViewModel实例的创建过程展示出来了。但是有个疑问,为什么不同的Fragment的获取到的是同一个ViewModel的实例?回答这个问题,我们按照图1进行逆向分析。
首先,ViewModel实例是ViewModelProvider.Factory工厂类创建的:
public open class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
) {
/**
* Implementations of `Factory` interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given `Class`.
*
* @param modelClass a `Class` whose instance is requested
* @return a newly created ViewModel
*/
public fun create(modelClass: Class): T
}
}
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this `ViewModelProvider`.
*
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param key The key to use to identify the ViewModel.
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @return A ViewModel that is an instance of the given type `T`.
*/
@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun get(key: String, modelClass: Class): 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
}
可以看到,get方法会先判断ViewModel实例是否已经存在,如果存在则直接返回,否则会通过工厂类Factory创建ViewModel实例并存到ViewModelStore的实例store中。这样一来,只要保证ViewModelStore的实例是同一个,那ViewModelProvider的get方法每次返回的都是同一个ViewModel实例。
我们再看ViewModelProvider的store是从哪里获取的,ViewModelProvider的get方法是在ViewModelLazy调用的,看ViewModelLazy的源码:
/**
* An implementation of [Lazy] used by [androidx.fragment.app.Fragment.viewModels] and
* [androidx.activity.ComponentActivity.viewmodels].
*
* [storeProducer] is a lambda that will be called during initialization, [VM] will be created
* in the scope of returned [ViewModelStore].
*
* [factoryProducer] is a lambda that will be called during initialization,
* returned [ViewModelProvider.Factory] will be used for creation of [VM]
*/
public class ViewModelLazy (
private val viewModelClass: KClass,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy {
private var cached: VM? = null
override val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
可见store是ViewModelLazy的构造函数中的参数storeProducer(高阶函数可当做参数传递)返回的。
而ViewModelLazy又是在FragmentViewModelLazy.kt的createViewModelLazy使用的:
@MainThread
inline fun Fragment.activityViewModels(
noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
/**
* Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
* to default factory.
*/
@MainThread
fun Fragment.createViewModelLazy(
viewModelClass: KClass,
storeProducer: () -> ViewModelStore,
factoryProducer: (() -> Factory)? = null
): Lazy {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
可以看到,storeProducer是扩展函数activityViewModels中通过requireActivity().viewModelStore 获取的。只要这些Fragment是同一个宿主Activity,那获取到的viewModelStore就是同一个,最终通过activityViewModels获取到的ViewModel实例也是同一个。
根据以上分析,我们就可以依葫芦画瓢实现Application作用域的共享ViewModel用于Activity和Fragment的相互通信了。
首先创建一个ApplicationViewModelLazy.kt文件,内容如下:
/**
* Returns a property delegate to access application's [ViewModel],
* if [factoryProducer] is specified then [ViewModelProvider.Factory]
* returned by it will be used to create [ViewModel] first time. Otherwise, the BaseViewModelApplication's
* [com.nxg.mvvm.BaseViewModelApplication.getDefaultViewModelProviderFactory](default factory)
* will be used.
*
* ```
* class MyAppCompatActivity : AppCompatActivity() {
* val viewmodel: MyViewModel by applicationViewModels()
* }
* ```
*
* This property can be accessed only after this AppCompatActivity is create i.e., after
* [AppCompatActivity.onCreate()], and access prior to that will result in IllegalArgumentException.
*/
@MainThread
inline fun AppCompatActivity.applicationViewModels(
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
) = createViewModelLazy(VM::class, { (applicationContext as BaseViewModelApplication).viewModelStore },
factoryProducer ?: { (applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory })
/**
* Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
* to default factory.
*/
@MainThread
fun AppCompatActivity.createViewModelLazy(
viewModelClass: KClass,
storeProducer: () -> ViewModelStore,
factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy {
val factoryPromise = factoryProducer ?: {
(applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
/**
* Returns a property delegate to access application's [ViewModel],
* if [factoryProducer] is specified then [ViewModelProvider.Factory]
* returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
* [com.nxg.mvvm.BaseViewModelApplication.getDefaultViewModelProviderFactory](default factory)
* will be used.
*
* ```
* class MyFragment : Fragment() {
* val viewmodel: MyViewModel by applicationViewModels()
* }
* ```
*
* This property can be accessed only after this Fragment is attached i.e., after
* [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException.
*/
@MainThread
inline fun Fragment.applicationViewModels(
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
) = createViewModelLazy(VM::class, { (requireActivity().applicationContext as BaseViewModelApplication).viewModelStore },
factoryProducer ?: { (requireActivity().applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory })
/**
* Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
* to default factory.
*/
@MainThread
fun Fragment.createViewModelLazy(
viewModelClass: KClass,
storeProducer: () -> ViewModelStore,
factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy {
val factoryPromise = factoryProducer ?: {
(requireActivity().applicationContext as BaseViewModelApplication).defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
定义了两个扩展方法:AppCompatActivity.applicationViewModels和Fragment.applicationViewModels,用法和activityViewModels是一样的,但提供的viewModelStore是不一样的,可以看到applicationViewModels的viewModelStore是BaseViewModelApplication提供的。
BaseViewModelApplication代码如下:
open class BaseViewModelApplication : Application(), ViewModelStoreOwner,HasDefaultViewModelProviderFactory{
companion object {
const val TAG = "BaseViewModelApplication"
}
// Lazily recreated from NonConfigurationInstances by getViewModelStore()
private var mViewModelStore: ViewModelStore? = null
private var mDefaultFactory: ViewModelProvider.Factory? = null
/**
* Returns the [ViewModelStore] associated with this application
*
*
* Overriding this method is no longer supported and this method will be made
* `final` in a future version of ComponentActivity.
*
* @return a `ViewModelStore`
* @throws IllegalStateException if called before the Activity is attached to the Application
* instance i.e., before onCreate()
*/
@NonNull
@Override
override fun getViewModelStore(): ViewModelStore {
ensureViewModelStore()
return mViewModelStore as ViewModelStore
}
/**
* Application不需要处理配置改变导致的重建
*/
private fun /* synthetic access */ensureViewModelStore() {
if (mViewModelStore == null) {
mViewModelStore = ViewModelStore()
}
}
override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory {
if(mDefaultFactory == null){
mDefaultFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(this)
}
return mDefaultFactory as ViewModelProvider.Factory
}
}
BaseViewModelApplication实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,用于提供ViewModelStore和ViewModelProvider.Factory 实例。这样一来,AppCompatActivity.applicationViewModels和Fragment.applicationViewModels获取到的都是保存在BaseViewModelApplication中ViewModelStore的同一个ViewModel实例。
在app模块中自定义Application继承BaseViewModelApplication。
@HiltAndroidApp
class App : BaseViewModelApplication() {
companion object {
const val TAG = "AppApplication"
private var INSTANCE: App by NotNullSingleValueVar()
fun instance() = INSTANCE
}
//定义一个属性管理类,进行非空和重复赋值的判断
private class NotNullSingleValueVar : ReadWriteProperty {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("application not initialized")
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = if (this.value == null) value
else throw IllegalStateException("application already initialized")
}
}
override fun onCreate() {
super.onCreate()
INSTANCE = this
Utils.init(this)
LogUtil.enable = BuildConfig.DEBUG
registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
}
override fun onLowMemory() {
super.onLowMemory()
Log.i(
TAG,
"onLowMemory: "
)
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
Log.i(
TAG,
"onTrimMemory: "
)
}
private val mActivityLifecycleCallbacks: ActivityLifecycleCallbacks =
object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
Log.i(
TAG,
"onActivityCreated: " + activity::class.java.name
)
}
override fun onActivityStarted(activity: Activity) {
Log.i(
TAG,
"onActivityStarted: " + activity::class.java.name
)
}
override fun onActivityResumed(activity: Activity) {
Log.i(
TAG,
"onActivityResumed: " + activity::class.java.name
)
}
override fun onActivityPaused(activity: Activity) {
Log.i(
TAG,
"onActivityPaused: " + activity::class.java.name
)
}
override fun onActivityStopped(activity: Activity) {
Log.i(
TAG,
"onActivityStopped: " + activity::class.java.name
)
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
Log.i(
TAG,
"onActivitySaveInstanceState: " + activity::class.java.name
)
}
override fun onActivityDestroyed(activity: Activity) {
Log.i(
TAG,
"onActivityDestroyed: " + activity::class.java.name
)
}
}
}
注意,重写onCreate方法的时候别忘记调用super.onCreate()。
假设有一个名为AppShareViewModel的ViewModel:
/**
* 作用域范围为Application的共享ShareViewModel
* 使用方法:
* ```
* class MyAppCompatActivity : AppCompatActivity() {
* val appShareViewModel: AppShareViewModel by applicationViewModels()
* }
*
* class MyFragment : Fragment() {
* val appShareViewModel: AppShareViewModel by applicationViewModels()
* }
* ```
*/
class AppShareViewModel(application: Application) : AndroidViewModel(application) {
// Backing property to avoid state updates from other classes
private val _uiState = MutableStateFlow(UiState.HOME)
// The UI collects from this StateFlow to get its state updates
val uiState: StateFlow = _uiState
init {
viewModelScope.launch {
_uiState.value = UiState.HOME
}
}
fun setUiState(state: UiState) {
viewModelScope.launch {
_uiState.value = state
}
}
/**
* 界面状态
*/
enum class UiState {
PERMISSION,HOME, START, PAUSE, PLAY
}
}
在Activity和Fragment中获取AppShareViewModel的实例如下(支持Hilt):
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "MainActivity"
}
private lateinit var binding: MainActivityBinding
//作用域范围为Application的共享ShareViewModel
private val appShareViewModel: AppShareViewModel by applicationViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = MainActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
// Start a coroutine in the lifecycle scope
lifecycleScope.launch {
// repeatOnLifecycle launches the block in a new coroutine every time the
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Trigger the flow and start listening for values.
// Note that this happens when lifecycle is STARTED and stops
// collecting when the lifecycle is STOPPED
appShareViewModel.uiState.collect {
// New value received
when (it) {
AppShareViewModel.UiState.PERMISSION -> {
}
AppShareViewModel.UiState.HOME -> {
}
AppShareViewModel.UiState.START -> {
}
AppShareViewModel.UiState.PAUSE -> {
}
AppShareViewModel.UiState.PLAY -> {
}
}
}
}
}
}
}
@AndroidEntryPoint
class AudioRecordFragment : Fragment() {
companion object {
const val TAG = "AudioRecordFragment"
fun newInstance() = AudioRecordFragment()
}
//作用域范围为Application的共享ShareViewModel
private val appShareViewModel: AppShareViewModel by applicationViewModels()
//作用域范围为Activity的共享ShareViewModel
private val shareViewModel:ShareViewModel by activityViewModels()
private val audioRecordViewModel: AudioRecordViewModel by viewModels()
private var _binding: AudioRecordFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = AudioRecordFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
......
appShareViewModel.uiState.collect {
// New value received
when (it) {
AppShareViewModel.UiState.PERMISSION -> {
}
AppShareViewModel.UiState.HOME -> {
}
AppShareViewModel.UiState.START -> {
}
AppShareViewModel.UiState.PAUSE -> {
}
AppShareViewModel.UiState.PLAY -> {
}
}
}
}
}
@AndroidEntryPoint
class AudioRecordListFragment : Fragment() {
companion object {
const val TAG = "AudioRecordListFragment"
fun newInstance() = AudioRecordListFragment()
}
//作用域范围为Application的共享ShareViewModel
private val appShareViewModel: AppShareViewModel by applicationViewModels()
//作用域范围为Activity的共享ShareViewModel
private val shareViewModel:ShareViewModel by activityViewModels()
private val audioRecordListViewModel: AudioRecordListViewModel by viewModels()
private var _binding: AudioRecordListFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = AudioRecordListFragmentBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
appShareViewModel.uiState.collect {
// New value received
when (it) {
AppShareViewModel.UiState.PERMISSION -> {
}
AppShareViewModel.UiState.HOME -> {
}
AppShareViewModel.UiState.START -> {
}
AppShareViewModel.UiState.PAUSE -> {
}
AppShareViewModel.UiState.PLAY -> {
}
}
}
......
}
}
然后可以愉快玩耍了。
通过模仿activityViewModels源码,我们定义了两个扩展方法:AppCompatActivity.applicationViewModels和Fragment.applicationViewModels,实现了Application作用域的ViewModel,非常简单。但是这样做是不是对的?会不会带来其它问题比如内存泄露啥的?毕竟ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据,可在发生屏幕旋转等配置更改后继续留存。我们这么一搞,好像是违背了这个中心思想,后续笔者会另外写一篇文章分析这个问题,敬请期待。
写在最后,首先非常感谢您耐心阅读完整篇文章,坚持写原创且基于实战的文章不是件容易的事,如果本文刚好对您有点帮助,欢迎您给文章点赞评论,您的鼓励是笔者坚持不懈的动力。若文章有不对之处也欢迎指正,再次感谢。
学如逆水行舟,不进则退;心似平原走马,易放难追。