詳解Android如何設(shè)計(jì)一個(gè)全局可調(diào)用的ViewModel對象
一、思路
viewModel
對象是存儲在ViewModelStore
中的,那么如果我們創(chuàng)建一個(gè)全局使用的ViewModelStore
并且在獲取viewModel
對象的時(shí)候從它里面獲取就可以了。
viewModel
是通過ViewModelProvider
的get
方法獲取的,一般是ViewModelProvider(owner: ViewModelStoreOwner, factory: Factory).get(ViewModel::class.java)
。
如何將ViewModelProvider
與ViewModelStore
關(guān)聯(lián)起來? 紐帶就是ViewModelStoreOwner
, ViewModelStoreOwner
是一個(gè)接口,需要實(shí)現(xiàn)getViewModelStore()
方法,而該方法返回的就是ViewModelStore
:
public interface ViewModelStoreOwner { /** * Returns owned {@link ViewModelStore} * * @return a {@code ViewModelStore} */ @NonNull ViewModelStore getViewModelStore(); //返回一個(gè)ViewModelStore }
讓某個(gè)類實(shí)現(xiàn)這個(gè)接口,重寫方法返回我們定義的ViewModelStore
就可以了。
至于上面ViewModelProvider
構(gòu)造方法的第二個(gè)參數(shù)Factory
是什么呢?
源碼中提供了二種Factory
,一種是NewInstanceFactory
,一種是AndroidViewModelFactory
,它們的主要區(qū)別是:
NewInstanceFactory創(chuàng)建ViewModel時(shí),會為每個(gè)Activity或Fragment創(chuàng)建一個(gè)新的ViewModel實(shí)例,這會導(dǎo)致ViewModel無法在應(yīng)用程序的不同部分共享數(shù)據(jù)。(ComponentActivity源碼getDefaultViewModelProviderFactory方法)
AndroidViewModelFactory可以訪問應(yīng)用程序的全局狀態(tài),并且ViewModel實(shí)例可以在整個(gè)應(yīng)用程序中是共享的。
根據(jù)我們的需求,需要用的是AndroidViewModelFactory。
二、具體實(shí)現(xiàn)
1、方式一:可以全局添加和獲取任意ViewModel
定義Application,Ktx.kt
文件
import android.app.Application lateinit var appContext: Application fun setApplicationContext(context: Application) { appContext = context }
定義全局可用的ViewModelOwner
實(shí)現(xiàn)類
object ApplicationScopeViewModelProvider : ViewModelStoreOwner { private val eventViewModelStore: ViewModelStore = ViewModelStore() override fun getViewModelStore(): ViewModelStore { return eventViewModelStore } private val mApplicationProvider: ViewModelProvider by lazy { ViewModelProvider( ApplicationScopeViewModelProvider, ViewModelProvider.AndroidViewModelFactory.getInstance(appContext) ) } fun <T : ViewModel> getApplicationScopeViewModel(modelClass: Class<T>): T { return mApplicationProvider.get(modelClass) } }
定義一個(gè)ViewModel
通過StateFlow
定義發(fā)送和訂閱事件的方法
class EventViewModel : ViewModel() { private val mutableStateFlow = MutableStateFlow(0) fun postEvent(state: Int) { mutableStateFlow.value = state } fun observeEvent(scope: CoroutineScope? = null, method: (Int) -> Unit = { _ -> }) { val eventScope = scope ?: viewModelScope eventScope.launch { mutableStateFlow.collect { method.invoke(it) } } } }
定義一個(gè)調(diào)用的類
object FlowEvent { //發(fā)送事件 fun postEvent(state: Int) { ApplicationScopeViewModelProvider.getApplicationScopeViewModel(EventViewModel::class.java) .postEvent(state) } //訂閱事件 fun observeEvent(scope: CoroutineScope? = null, method: (Int) -> Unit = { _ -> }) { ApplicationScopeViewModelProvider.getApplicationScopeViewModel(EventViewModel::class.java) .observeEvent(scope, method) } }
測試代碼如下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //打印協(xié)程名稱 System.setProperty("kotlinx.coroutines.debug", "on") FlowEvent.observeEvent { printMsg("MainActivity observeEvent before :$it") } //修改值 FlowEvent.postEvent(1) FlowEvent.observeEvent { printMsg("MainActivity observeEvent after :$it") } } } //日志 內(nèi)容:MainActivity observeEvent before :0 線程:main @coroutine#1 內(nèi)容:MainActivity observeEvent before :1 線程:main @coroutine#1 內(nèi)容:MainActivity observeEvent after :1 線程:main @coroutine#2
2、方式二:更方便在Activity和Fragment中調(diào)用
定義Application,讓BaseApplication
實(shí)現(xiàn)ViewModelStoreOwner
//BaseApplication實(shí)現(xiàn)ViewModelStoreOwner接口 class BaseApplication : Application(), ViewModelStoreOwner { private lateinit var mAppViewModelStore: ViewModelStore private var mFactory: ViewModelProvider.Factory? = null override fun onCreate() { super.onCreate() //設(shè)置全局的上下文 setApplicationContext(this) //創(chuàng)建ViewModelStore mAppViewModelStore = ViewModelStore() } override fun getViewModelStore(): ViewModelStore = mAppViewModelStore /** * 獲取一個(gè)全局的ViewModel */ fun getAppViewModelProvider(): ViewModelProvider { return ViewModelProvider(this, this.getAppFactory()) } private fun getAppFactory(): ViewModelProvider.Factory { if (mFactory == null) { mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(this) } return mFactory as ViewModelProvider.Factory } }
Ktx.kt
文件也有變化,如下
lateinit var appContext: Application fun setApplicationContext(context: Application) { appContext = context } //定義擴(kuò)展方法 inline fun <reified VM : ViewModel> Fragment.getAppViewModel(): VM { (this.requireActivity().application as? BaseApplication).let { if (it == null) { throw NullPointerException("Application does not inherit from BaseApplication") } else { return it.getAppViewModelProvider().get(VM::class.java) } } } //定義擴(kuò)展方法 inline fun <reified VM : ViewModel> AppCompatActivity.getAppViewModel(): VM { (this.application as? BaseApplication).let { if (it == null) { throw NullPointerException("Application does not inherit from BaseApplication") } else { return it.getAppViewModelProvider().get(VM::class.java) } } }
在BaseActivity
和BaseFragment
中調(diào)用上述擴(kuò)展方法
abstract class BaseActivity: AppCompatActivity() { //創(chuàng)建ViewModel對象 val eventViewModel: EventViewModel by lazy { getAppViewModel() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } }
abstract class BaseFragment: Fragment() { //創(chuàng)建ViewModel對象 val eventViewModel: EventViewModel by lazy { getAppViewModel() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } }
測試代碼
class MainActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //打印協(xié)程名稱 System.setProperty("kotlinx.coroutines.debug", "on") eventViewModel.observeEvent { printMsg("MainActivity observeEvent :$it") } findViewById<AppCompatButton>(R.id.bt).setOnClickListener { //點(diǎn)擊按鈕修改值 eventViewModel.postEvent(1) //跳轉(zhuǎn)到其他Activity Intent(this, TwoActivity::class.java).also { startActivity(it) } } } }
class TwoActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_two) eventViewModel.observeEvent { printMsg("TwoActivity observeEvent :$it") } } }
日志
內(nèi)容:MainActivity observeEvent :0 線程:main @coroutine#1 內(nèi)容:MainActivity observeEvent :1 線程:main @coroutine#1 內(nèi)容:TwoActivity observeEvent :1 線程:main @coroutine#2
以上就是詳解Android如何設(shè)計(jì)一個(gè)全局可調(diào)用的ViewModel對象的詳細(xì)內(nèi)容,更多關(guān)于Android ViewModel對象的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Github簡單易用的?Android?ViewModel?Retrofit框架
這篇文章主要介紹了Github簡單易用的Android?ViewModel?Retrofit框架,RequestViewMode有自動(dòng)對LiveData進(jìn)行緩存管理,每個(gè)retrofit api接口復(fù)用一個(gè)livedata的優(yōu)勢。下文具體詳情,感興趣的小伙伴可以參考一下2022-06-06Android簡單創(chuàng)建一個(gè)Activity的方法
這篇文章主要介紹了Android簡單創(chuàng)建一個(gè)Activity的方法,結(jié)合圖文形式分析了Android創(chuàng)建Activity的具體步驟與實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-04-04Android實(shí)現(xiàn)退出時(shí)關(guān)閉所有Activity的方法
這篇文章主要介紹了Android實(shí)現(xiàn)退出時(shí)關(guān)閉所有Activity的方法,主要通過自定義類CloseActivityClass實(shí)現(xiàn)這一功能,需要的朋友可以參考下2014-09-09Android消息通知Notification常用方法(發(fā)送消息和接收消息)
最近在做消息通知類Notification的相關(guān)業(yè)務(wù),利用閑暇時(shí)間總結(jié)一下,主要分為兩部分來記錄:發(fā)送消息和接收消息,對Android消息通知相關(guān)知識感興趣的朋友一起看看吧2024-02-02Android 中ListView點(diǎn)擊Item無響應(yīng)問題的解決辦法
如果listitem里面包括button或者checkbox等控件,默認(rèn)情況下listitem會失去焦點(diǎn),導(dǎo)致無法響應(yīng)item的事件,怎么解決呢?下面小編給大家分享下listview點(diǎn)擊item無響應(yīng)的解決辦法2016-12-12Android仿新聞頂部導(dǎo)航標(biāo)簽切換效果
這篇文章主要為大家詳細(xì)介紹了Android仿新聞頂部導(dǎo)航標(biāo)簽切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android基礎(chǔ)總結(jié)篇之三:Activity的task相關(guān)介紹
這篇文章主要介紹了Android基礎(chǔ)總結(jié)篇之三:Activity的task相關(guān)介紹,具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11