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)的時候才會回調相應的方法,從而刷新相應的 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() } }
這里,我們不糾結代碼中的細節(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),當我們在 MainActivity.kt
中使用 ViewModelProviders
聲明 viewModel
時,我們沒有調用任何 viewModel
的構造函數(shù)。這是因為,ViewModelProviders
在內部為我們管理并調用 ViewModel
的主要構造函數(shù)(primary constructor)并創(chuàng)建 ViewModel
的實例并將實例返回。
如果我們將參數(shù)傳遞給 viewModel
的構造函數(shù)時,其他都不變,這個時候,系統(tǒng)會報錯:RunTimeError。之所以會有這個報錯是因為 ViewModelProviders.of()
方法在內部創(chuàng)建默認的 ViewModelProvider.Factory
實現(xiàn)來創(chuàng)建我們的沒有參數(shù)的 ViewModel
(再次注意,這里的 ViewModel 是沒有參數(shù)的)。 因此,當我們在構造函數(shù)中添加參數(shù)時,ViewModelProvider.Factory
的內部實現(xiàn)無法初始化我們這個 ViewModel
,因為 ViewModelProvider.Factory
調用了創(chuàng)建 ViewModel
實例的主構造函數(shù)。
所以說,如果在構造函數(shù)中添加參數(shù),則必須創(chuàng)建自己的 ViewModelProvider.Factory
實現(xiàn)來創(chuàng)建 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") } }
這里有幾點需要注意:
我們可以通過構造函數(shù)或我們喜歡的任何其他模式(Singleton、FactoryPattern 等)將 ViewModel
參數(shù)傳遞給 ViewModelProvider.Factory
。 這是因為我們在初始化 ViewModel
時無法在 Activity
或 Fragment
中調用 ViewModel
構造函數(shù),并且我們想設置 ViewModel
構造函數(shù)的參數(shù)值,因此我們需要將參數(shù)值傳遞給
ViewModelProvider.Factory
,它將創(chuàng)建 ViewModel
。ViewModelProvider.Factory
是一個具有 create
方法的接口。 create
方法負責創(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 結論
現(xiàn)在我們應該很清楚 ViewModelProvider.Factory
的作用和使用方式了。這里做一個簡單的總結:
何時使用 ViewModelProvider.Factory
?
如果我們的 ViewModel 有依賴項或參數(shù)傳遞,那么我們應該通過構造函數(shù)傳遞此依賴項(這是傳遞依賴項的最佳方式)。這個時候 ViewModelProvider.Factory
需要被使用。
何時不使用 ViewModelProvider.Factory
?
如果我們的 ViewModel 沒有依賴項或參數(shù)傳遞,那么我們將不需要創(chuàng)建自己的 ViewModelProvider.Factory
。默認會自動為我們創(chuàng)建 ViewModel。
到此這篇關于Kotlin ViewModelProvider.Factory的使用實例詳解的文章就介紹到這了,更多相關Kotlin ViewModelProvider.Factory內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
一行代碼教你解決Scrollview和TextInput焦點獲取問題
這篇文章主要為大家介紹了一行代碼教你解決Scrollview和TextInput焦點獲取問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12Android 仿淘寶、京東商品詳情頁向上拖動查看圖文詳情控件DEMO詳解
本文給大家介紹android 仿淘寶、京東商品詳情頁向上拖動查看圖文詳情控件DEMO詳解,使用兩個scrollView,兩個scrollView 豎直排列,通過自定義viewGroup來控制兩個scrollView的豎直排列,以及滑動事件的處理。對android 拖動查看圖文詳情知識感興趣的朋友一起學習吧2016-09-09Android SpannableString設置超鏈接、顏色、字體等屬性
這篇文章主要介紹了Android SpannableString設置超鏈接、顏色、字體等屬性的相關資料,需要的朋友可以參考下2017-01-01Android顏色處理SweepGradient掃描及梯度渲染示例
這篇文章主要為大家介紹了Android顏色處理SweepGradient掃描渲染及梯度渲染示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06Android開發(fā)之AlertDialog實現(xiàn)彈出對話框
這篇文章主要為大家詳細介紹了Android開發(fā)之AlertDialog實現(xiàn)彈出對話框,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-09-09