Kotlin ViewModelProvider.Factory的使用實(shí)例詳解
這里,我們將介紹 Kotlin ViewModelProvider.Factory 的作用和使用方式。
在我們使用 ViewModel 的時(shí)候,我們會發(fā)現(xiàn),有的時(shí)候我們需要用到 ViewModelFactory,有的時(shí)候不需要。
這里,我們將介紹 Kotlin ViewModelProvider.Factory 的作用和使用方式。
在我們使用 ViewModel 的時(shí)候,我們會發(fā)現(xiàn),有的時(shí)候我們需要用到 ViewModelFactory,有的時(shí)候不需要。
1 沒有使用到 ViewModelFactory 的例子
下面這個(gè)例子中,我們沒有使用到 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,可以做到在組件處于激活狀態(tài)的時(shí)候才會回調(diào)相應(yīng)的方法,從而刷新相應(yīng)的 UI
val countries = MutableLiveData<List<Country>>()
val countryLoadError = MutableLiveData<String?>()
val loading = MutableLiveData<Boolean>()
fun refresh() {
fetchCountries()
}
private fun fetchCountries() {
loading.value = true
// 通過launch啟動一個(gè)攜程回返回一個(gè)Job類型的對象實(shí)例,我們可以通過job.start()來啟動攜程(如果launch(start = CoroutineStart.LAZY)
// 這么設(shè)置的話),可以通過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()
}
}
這里,我們不糾結(jié)代碼中的細(xì)節(jié),只觀察 viewModel 如何被定義和使用。
2 使用到 ViewModelFactory 的例子
下面這個(gè)例子中,我們、使用到了 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 分析
我們發(fā)現(xiàn),當(dāng)我們在 MainActivity.kt 中使用 ViewModelProviders 聲明 viewModel 時(shí),我們沒有調(diào)用任何 viewModel 的構(gòu)造函數(shù)。這是因?yàn)椋?code>ViewModelProviders 在內(nèi)部為我們管理并調(diào)用 ViewModel 的主要構(gòu)造函數(shù)(primary constructor)并創(chuàng)建 ViewModel 的實(shí)例并將實(shí)例返回。
如果我們將參數(shù)傳遞給 viewModel 的構(gòu)造函數(shù)時(shí),其他都不變,這個(gè)時(shí)候,系統(tǒng)會報(bào)錯(cuò):RunTimeError。之所以會有這個(gè)報(bào)錯(cuò)是因?yàn)?ViewModelProviders.of() 方法在內(nèi)部創(chuàng)建默認(rèn)的 ViewModelProvider.Factory 實(shí)現(xiàn)來創(chuàng)建我們的沒有參數(shù)的 ViewModel(再次注意,這里的 ViewModel 是沒有參數(shù)的)。 因此,當(dāng)我們在構(gòu)造函數(shù)中添加參數(shù)時(shí),ViewModelProvider.Factory 的內(nèi)部實(shí)現(xiàn)無法初始化我們這個(gè) ViewModel,因?yàn)?ViewModelProvider.Factory 調(diào)用了創(chuàng)建 ViewModel 實(shí)例的主構(gòu)造函數(shù)。
所以說,如果在構(gòu)造函數(shù)中添加參數(shù),則必須創(chuàng)建自己的 ViewModelProvider.Factory 實(shí)現(xiàn)來創(chuàng)建 ViewModel 實(shí)例。
那么,什么是 ViewModelProvider.Factory?還是剛才的第二個(gè)例子,我們把相關(guān)的代碼復(fù)制在下面:
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")
}
}
這里有幾點(diǎn)需要注意:
我們可以通過構(gòu)造函數(shù)或我們喜歡的任何其他模式(Singleton、FactoryPattern 等)將 ViewModel 參數(shù)傳遞給 ViewModelProvider.Factory。 這是因?yàn)槲覀冊诔跏蓟?ViewModel 時(shí)無法在 Activity 或 Fragment 中調(diào)用 ViewModel 構(gòu)造函數(shù),并且我們想設(shè)置 ViewModel 構(gòu)造函數(shù)的參數(shù)值,因此我們需要將參數(shù)值傳遞給
ViewModelProvider.Factory,它將創(chuàng)建 ViewModel。ViewModelProvider.Factory 是一個(gè)具有 create 方法的接口。 create 方法負(fù)責(zé)創(chuàng)建我們的 VeiwModel 的實(shí)例。
我們在LoginFragment.kt中是這么實(shí)例化 ViewModel 的:
val factory = LoginViewModelFactory(repository, application) loginViewModel = ViewModelProvider(this, factory).get(LoginViewModel::class.java)
我們將我們的參數(shù)或依賴項(xiàng)傳遞給我們的 ViewModelProvider.Factory,以便它能夠?yàn)槲覀儎?chuàng)建 ViewModel。 ViewModelProviders.of(context, factory) 方法獲取我們的 ViewModelProvider.Factory 的實(shí)例。
4 結(jié)論
現(xiàn)在我們應(yīng)該很清楚 ViewModelProvider.Factory 的作用和使用方式了。這里做一個(gè)簡單的總結(jié):
何時(shí)使用 ViewModelProvider.Factory?
如果我們的 ViewModel 有依賴項(xiàng)或參數(shù)傳遞,那么我們應(yīng)該通過構(gòu)造函數(shù)傳遞此依賴項(xiàng)(這是傳遞依賴項(xiàng)的最佳方式)。這個(gè)時(shí)候 ViewModelProvider.Factory 需要被使用。
何時(shí)不使用 ViewModelProvider.Factory?
如果我們的 ViewModel 沒有依賴項(xiàng)或參數(shù)傳遞,那么我們將不需要創(chuàng)建自己的 ViewModelProvider.Factory。默認(rèn)會自動為我們創(chuàng)建 ViewModel。
到此這篇關(guān)于Kotlin ViewModelProvider.Factory的使用實(shí)例詳解的文章就介紹到這了,更多相關(guān)Kotlin ViewModelProvider.Factory內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)短信驗(yàn)證碼自動填寫功能
這篇文章主要介紹了Android實(shí)現(xiàn)短信驗(yàn)證碼自動填寫功能,感興趣的小伙伴們可以參考一下2015-12-12
Android開發(fā)手冊Button按鈕實(shí)現(xiàn)點(diǎn)擊音效
這篇文章主要為大家介紹了Android開發(fā)手冊Button按鈕實(shí)現(xiàn)點(diǎn)擊音效示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Android編譯出現(xiàn)Warning:Mapping?new?ns?to?old?ns報(bào)錯(cuò)的解決方案
android在編譯的過程中難免會出現(xiàn)些錯(cuò)誤,下面這篇文章主要給大家介紹了關(guān)于Android編譯出現(xiàn)Warning:Mapping?new?ns?to?old?ns報(bào)錯(cuò)的解決方案,需要的朋友可以參考下2023-02-02
Android開發(fā)之登錄驗(yàn)證實(shí)例教程
這篇文章主要介紹了Android開發(fā)之登錄驗(yàn)證實(shí)現(xiàn)方法,包括發(fā)送數(shù)據(jù)、服務(wù)器端驗(yàn)證、配置文件等,需要的朋友可以參考下2014-08-08
android layout XML解析錯(cuò)誤的解決方法
從別的地方復(fù)制過來XML時(shí),layout預(yù)覽時(shí)提示解析錯(cuò)誤。2013-04-04
Android 自定義一套 Dialog通用提示框 (代碼庫)
這篇文章主要介紹了Android 自定義一套 Dialog通用提示框 (代碼庫),需要的朋友可以參考下2017-04-04
Android開發(fā)之判斷有無虛擬按鍵(導(dǎo)航欄)的實(shí)例
下面小編就為大家分享一篇Android開發(fā)之判斷有無虛擬按鍵(導(dǎo)航欄)的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01

