網站首頁 編程語言 正文
這里,我們將介紹 Kotlin ViewModelProvider.Factory 的作用和使用方式。
在我們使用 ViewModel 的時候,我們會發現,有的時候我們需要用到 ViewModelFactory,有的時候不需要。
這里,我們將介紹 Kotlin ViewModelProvider.Factory 的作用和使用方式。
在我們使用 ViewModel 的時候,我們會發現,有的時候我們需要用到 ViewModelFactory,有的時候不需要。
1 沒有使用到 ViewModelFactory 的例子
下面這個例子中,我們沒有使用到 ViewModelFactory:
MainActivity.kt
class MainActivity : AppCompatActivity() { lateinit var viewModel: ListViewModel private val countriesAdapter = CountryListAdapter(arrayListOf()) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java) viewModel.refresh() countriesList.apply { layoutManager = LinearLayoutManager(context) adapter = countriesAdapter } observeViewModel() } ... }
ListViewModel.kt
:
class ListViewModel: ViewModel() { private val countriesService = CountriesService.getCountriesService() var job: Job? = null private val exceptionHandler = CoroutineExceptionHandler{ coroutineContext, throwable -> onError("Exception: ${throwable.localizedMessage}") } // 生命周期感知型組件 MutableLiveData,可以做到在組件處于激活狀態的時候才會回調相應的方法,從而刷新相應的 UI val countries = MutableLiveData<List<Country>>() val countryLoadError = MutableLiveData<String?>() val loading = MutableLiveData<Boolean>() fun refresh() { fetchCountries() } private fun fetchCountries() { loading.value = true // 通過launch啟動一個攜程回返回一個Job類型的對象實例,我們可以通過job.start()來啟動攜程(如果launch(start = CoroutineStart.LAZY) // 這么設置的話),可以通過job.cancel來取消攜程 job = CoroutineScope(Dispatchers.IO + exceptionHandler).launch { val response : Response<List<Country>> = countriesService.getCountries() // after we get the response, we hope that we could switch back to main thread and display on screen. withContext(Dispatchers.Main) { if (response.isSuccessful){ countries.value = response.body() countryLoadError.value = null loading.value = false } else { onError("Error: ${response.message()}") } } } } private fun onError(message: String) { countryLoadError.value = message loading.value = false } override fun onCleared() { super.onCleared() job?.cancel() } }
這里,我們不糾結代碼中的細節,只觀察 viewModel 如何被定義和使用。
2 使用到 ViewModelFactory 的例子
下面這個例子中,我們、使用到了 ViewModelFactory:
LoginViewModelFactory.kt
class LoginViewModelFactory( private val repository: RegisterRepository, private val application: Application ): ViewModelProvider.Factory{ @Suppress("Unchecked_cast") override fun <T : ViewModel?> create(modelClass: Class<T>): T { if(modelClass.isAssignableFrom(LoginViewModel::class.java)) { return LoginViewModel(repository, application) as T } throw IllegalArgumentException("Unknown View Model Class") } }
LoginViewModel.kt
class LoginViewModel(private val repository: RegisterRepository, application: Application) : AndroidViewModel(application), Observable { val users = repository.users @Bindable val inputUsername = MutableLiveData<String>() @Bindable val inputPassword = MutableLiveData<String>() private val viewModelJob = Job() private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) ... }
LoginFragment.kt
class LoginFragment : Fragment() { private lateinit var loginViewModel: LoginViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding: FragmentLoginBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_login, container, false ) val application = requireNotNull(this.activity).application val dao = RegisterDatabase.getInstance(application).registerDatabaseDao val repository = RegisterRepository(dao) val factory = LoginViewModelFactory(repository, application) loginViewModel = ViewModelProvider(this, factory).get(LoginViewModel::class.java) binding.myLoginViewModel = loginViewModel binding.lifecycleOwner = this loginViewModel.navigatetoRegister.observe(this, Observer { hasFinished-> if (hasFinished == true){ Log.i("MYTAG","insidi observe") displayUsersList() loginViewModel.doneNavigatingRegiter() } }) ... } }
3 分析
我們發現,當我們在 MainActivity.kt
中使用 ViewModelProviders
聲明 viewModel
時,我們沒有調用任何 viewModel
的構造函數。這是因為,ViewModelProviders
在內部為我們管理并調用 ViewModel
的主要構造函數(primary constructor)并創建 ViewModel
的實例并將實例返回。
如果我們將參數傳遞給 viewModel
的構造函數時,其他都不變,這個時候,系統會報錯:RunTimeError。之所以會有這個報錯是因為 ViewModelProviders.of()
方法在內部創建默認的 ViewModelProvider.Factory
實現來創建我們的沒有參數的 ViewModel
(再次注意,這里的 ViewModel 是沒有參數的)。 因此,當我們在構造函數中添加參數時,ViewModelProvider.Factory
的內部實現無法初始化我們這個 ViewModel
,因為 ViewModelProvider.Factory
調用了創建 ViewModel
實例的主構造函數。
所以說,如果在構造函數中添加參數,則必須創建自己的 ViewModelProvider.Factory
實現來創建 ViewModel 實例。
那么,什么是 ViewModelProvider.Factory
?還是剛才的第二個例子,我們把相關的代碼復制在下面:
LoginViewModelFactory.kt
class LoginViewModelFactory( private val repository: RegisterRepository, private val application: Application ): ViewModelProvider.Factory{ @Suppress("Unchecked_cast") override fun <T : ViewModel?> create(modelClass: Class<T>): T { if(modelClass.isAssignableFrom(LoginViewModel::class.java)) { return LoginViewModel(repository, application) as T } throw IllegalArgumentException("Unknown View Model Class") } }
這里有幾點需要注意:
我們可以通過構造函數或我們喜歡的任何其他模式(Singleton、FactoryPattern 等)將 ViewModel
參數傳遞給 ViewModelProvider.Factory
。 這是因為我們在初始化 ViewModel
時無法在 Activity
或 Fragment
中調用 ViewModel
構造函數,并且我們想設置 ViewModel
構造函數的參數值,因此我們需要將參數值傳遞給
ViewModelProvider.Factory
,它將創建 ViewModel
。ViewModelProvider.Factory
是一個具有 create
方法的接口。 create
方法負責創建我們的 VeiwModel
的實例。
我們在LoginFragment.kt
中是這么實例化 ViewModel 的:
val factory = LoginViewModelFactory(repository, application) loginViewModel = ViewModelProvider(this, factory).get(LoginViewModel::class.java)
我們將我們的參數或依賴項傳遞給我們的 ViewModelProvider.Factory
,以便它能夠為我們創建 ViewModel。 ViewModelProviders.of(context, factory)
方法獲取我們的 ViewModelProvider.Factory
的實例。
4 結論
現在我們應該很清楚 ViewModelProvider.Factory
的作用和使用方式了。這里做一個簡單的總結:
何時使用 ViewModelProvider.Factory
?
如果我們的 ViewModel 有依賴項或參數傳遞,那么我們應該通過構造函數傳遞此依賴項(這是傳遞依賴項的最佳方式)。這個時候 ViewModelProvider.Factory
需要被使用。
何時不使用 ViewModelProvider.Factory
?
如果我們的 ViewModel 沒有依賴項或參數傳遞,那么我們將不需要創建自己的 ViewModelProvider.Factory
。默認會自動為我們創建 ViewModel。
原文鏈接:https://blog.csdn.net/zyctimes/article/details/127577648
相關推薦
- 2022-01-31 torch.save實現對網絡結構和模型參數的保存 & pytorch模型文件.pt .pt
- 2022-09-17 Golang中的包及包管理工具go?mod詳解_Golang
- 2022-08-18 詳解如何從Matlab中導出清晰的結果圖片_C 語言
- 2022-09-19 Python正則表達式以及常用匹配實例_python
- 2023-07-07 什么是 AOP?對于 Spring IoC 和 AOP 的理解?
- 2022-08-24 Python?常見的配置文件寫法梳理匯總_python
- 2022-02-10 el-date-picker只能選擇當前時間及之前的時間
- 2022-03-16 C#?使用Fluent?API?創建自己的DSL(推薦)_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支