Kotlin ViewModelProvider.Factory的使用实例详解
破浪会有时 人气:0这里,我们将介绍 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。
加载全部内容