Kotlin ViewModelProvider.Factory的使用实例详解
作者:破浪会有时
这里,我们将介绍 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。
到此这篇关于Kotlin ViewModelProvider.Factory的使用实例详解的文章就介绍到这了,更多相关Kotlin ViewModelProvider.Factory内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!