Kotlin 与LiveData观察员、适配器、协同程序混淆
使用MVVM架构 我在想我应该如何完成这项任务时遇到了困难。我的想法是从RESTAPI检索歌曲列表,将该列表传递到SongListAdapter,SongListAdapter使用歌曲列表创建自定义回收器视图,然后膨胀该视图 在这之前就像Kotlin 与LiveData观察员、适配器、协同程序混淆,kotlin,mvvm,android-livedata,kotlin-coroutines,Kotlin,Mvvm,Android Livedata,Kotlin Coroutines,使用MVVM架构 我在想我应该如何完成这项任务时遇到了困难。我的想法是从RESTAPI检索歌曲列表,将该列表传递到SongListAdapter,SongListAdapter使用歌曲列表创建自定义回收器视图,然后膨胀该视图 在这之前就像 //Get Songs CoroutineScope(IO).launch { val songList = viewModel.getSongs() wit
//Get Songs
CoroutineScope(IO).launch {
val songList = viewModel.getSongs()
withContext(Main){
val rvSongs = rvSongs as RecyclerView
val adapter = SongListAdapter(songList)
rvSongs.adapter = adapter
rvSongs.layoutManager = LinearLayoutManager(this@SongListActivity)
}
}
但是现在我想用liveData来做这件事,而val songList=viewModel.getSongs()
并没有切断它。相反,我需要使用
viewModel.getSongs().observe(this@SongListActivity, Observer {
//UI Stuff
}
问题是,getSongs()
点击api端点来检索列表,这需要在后台线程上完成。但是不能在后台线程上调用观察器。。。这让我觉得我处理这件事的方式完全错了
我在想我可以有两种不同的功能来获取歌曲。。。一个命中端点,另一个将列表
数据转换为可变livedata
,但这感觉太复杂了。我应该如何处理livedata
歌曲列表活动
class SongListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.song_list) //I changed this for testing, change back to activity_main
//Set View Model
val viewModel = ViewModelProvider(this).get(SongListActivityViewModel::class.java)
//Get Songs
CoroutineScope(IO).launch{
//Need to hit getSongs() endpoint here
withContext(Main){
val rvSongs = rvSongs as RecyclerView
val adapter = SongListAdapter(songList)
rvSongs.adapter = adapter
rvSongs.layoutManager = LinearLayoutManager(this@SongListActivity)
}
}
}
}
SongListActivityViewModel
class SongListActivityViewModel : ViewModel() {
private val songLiveData = MutableLiveData<List<Song>?>()
//Returns list of songs on the first page.
suspend fun getSongs(): MutableLiveData<List<Song>?> {
//Hit Endpoint
val songPage1 = FunkwhaleRepository.getSongs()
//Store as LiveData
songLiveData.postValue(songPage1.results)
return songLiveData
}
}
class SongListActivityViewModel:ViewModel(){
private val songLiveData=MutableLiveData()
//返回第一页上的歌曲列表。
suspend fun getSongs():MutableLiveData{
//命中端点
val songPage1=funkwalerepository.getSongs()
//存储为LiveData
songLiveData.postValue(songPage1.results)
返回songLiveData
}
}
首先,我认为您应该考虑使用适当的Android生命周期范围来启动您的协同程序。如果您在Android组件中创建了一个作用域,比如活动
,那么您应该在活动被销毁时自行取消它。有一些很好的文档,我建议大家看一下
关于你的实际问题,你的思路是正确的
首先,您的ViewModel
应该像在代码中一样公开一些LiveData
,但应该在其init
块中启动一个从端点获取数据的协同路由作用域。然后,当它获取数据时,您应该只将LiveData
值设置为加载的数据
在您的活动中,不要尝试启动协同程序来设置您的RecyclerView
,只需事先使用LayoutManager
进行设置,然后开始观察公开的LiveData
。然后,当观察到的数据发生更改时,才将适配器设置为RecyclerView
快速的伪代码概述可能有助于:
视图模型
ViewModel {
val liveData = MutableLiveData<>()
init {
scope.launch {
liveData.value = api.fetch()
}
}
}
注意:您也可以使用
liveData(LiveDataScope.(->)Unit)
builder函数简化ViewModel
代码,但我建议您了解这两种方法以及它们的区别。很好奇为什么您要从协同程序切换到liveData,当这么多专家建议大家开始从LIveData过渡到协同程序时,我们已经有了SharedFlow。顺便说一句,您的示例协同程序代码中有几个错误。@Tenfour04您能对此进行一点扩展吗?我一直在youtube上关注Mitch的编码,他通常都在谈论在他的旧视频中使用livedata。我是android开发新手,所以我对SharedFlow一无所知。SharedFlow非常新(大约三个月)。它及其子类StateFlow可以用作LiveData的替代品。最近推荐它的原因是它有助于打破您的存储库,并查看模型层对Android特定工具的依赖性。LiveData可能会保留一段时间,因为协同路由和SharedFlow只是Kotlin。但我认为LiveData最终有可能会被弃用,Google会告诉所有Java开发人员,“太糟糕了,换成Kotlin吧。”这让我觉得我应该考虑一下!推荐一些关于这个主题的有用的初学者指南吗?我可以稍后再看并提出一些建议。我想我刚刚从官方文档和Roman Elizarov的一篇中篇文章中了解到。对于scope
,我建议只使用viewModelScope
,并将其保存在主调度器上。OP在其原始协同程序代码中的错误在于未使用lifecycleScope
或coroutineScope
,而是创建一个新的作用域,如果抓取超过了活动时间,它将泄漏视图。@Tenfour04我只是给出了一个结构概述,而不是实现细节,所以我不是说“作用域”是字面意义上的,在这种情况下,viewModelScope
将是正确的选择。这种方法对我很有效!谢谢你给我指点我犯的一些错误@发呆的愤怒没问题!关于SharedFlow
等内容的简要说明。在这里使用LiveData
非常合适,只要它位于表示层。然而,如果要实现一个存储库模式来获取数据,那么应该使用Flow
@HenryTwist的一些实现,我实际上可能正在使用存储库模式。当运行viewModel.init()
设置livedata时,它会点击repository.getSongs()
,然后点击apiInterface.getSongs()
,它使用Refught2实际点击端点。在这种情况下,我应该研究实现流程吗?