文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

KotlinViewModelProvider.Factory的使用实例详解

2023-02-17 12:03

关注

这里,我们将介绍 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 时无法在 ActivityFragment 中调用 ViewModel 构造函数,并且我们想设置 ViewModel 构造函数的参数值,因此我们需要将参数值传递给

ViewModelProvider.Factory,它将创建 ViewModelViewModelProvider.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内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯