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

