与 LiveData Observers、Adapters、Coroutines 混淆
Confusion with LiveData Observers, Adapters, Coroutines
使用 MVVM 架构。
我无法弄清楚我应该如何完成这项任务。我的想法是,我想从 REST API 检索歌曲列表,将该列表传递到使用歌曲列表创建自定义回收器视图的 SongListAdapter,然后扩充该视图。
以前像
一样简单
//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 端点以检索列表,这需要在后台线程上完成。但是不能在后台线程上调用观察者......这让我觉得我处理这个问题的方式是错误的。
我在想我可以有两个不同的函数来获取歌曲...一个到达终点,另一个将 List<Song>
数据转换为 MutableLiveData<List<Song>>
但感觉就像我太复杂了。我应该如何使用实时数据执行此操作?
歌曲列表活动
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
}
}
首先,我认为您应该看一下使用适当的 Android 生命周期范围来启动协程。如果您在 Android 组件中创建一个作用域,例如 Activity
,那么您应该考虑在 activity 被销毁时自行取消它。有一些很好的文档 here 我建议您看一看。
关于你的实际问题,你是在正确的轨道上!
首先,您的 ViewModel
应该像在您的代码中那样公开一些 LiveData
,但应该在其 init
块中启动协程作用域,从端点获取数据.然后当它获取它时,您应该只将 LiveData
值设置为您加载的数据。
在您的 activity 中,与其尝试启动协程来设置您的 RecyclerView
,不如仅使用 LayoutManager
预先设置它,然后开始观察暴露的 [=14] =].然后当被观察的数据发生变化时,才将适配器设置为 RecyclerView
.
快速伪代码概述可能会有所帮助:
ViewModel
ViewModel {
val liveData = MutableLiveData<>()
init {
scope.launch {
liveData.value = api.fetch()
}
}
}
Activity
Activity {
onCreate {
...
recyclerView.layoutManager = LinearLayoutManager
...
viewModel.liveData.observe {
recyclerView.adapter = Adapter(data)
}
}
}
注意: 您也可以使用 liveData(LiveDataScope<T>.() -> Unit)
构建器函数来简化 ViewModel
代码,但我建议您了解这两种方法以及它们有何不同。
使用 MVVM 架构。
我无法弄清楚我应该如何完成这项任务。我的想法是,我想从 REST API 检索歌曲列表,将该列表传递到使用歌曲列表创建自定义回收器视图的 SongListAdapter,然后扩充该视图。
以前像
一样简单//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 端点以检索列表,这需要在后台线程上完成。但是不能在后台线程上调用观察者......这让我觉得我处理这个问题的方式是错误的。
我在想我可以有两个不同的函数来获取歌曲...一个到达终点,另一个将 List<Song>
数据转换为 MutableLiveData<List<Song>>
但感觉就像我太复杂了。我应该如何使用实时数据执行此操作?
歌曲列表活动
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
}
}
首先,我认为您应该看一下使用适当的 Android 生命周期范围来启动协程。如果您在 Android 组件中创建一个作用域,例如 Activity
,那么您应该考虑在 activity 被销毁时自行取消它。有一些很好的文档 here 我建议您看一看。
关于你的实际问题,你是在正确的轨道上!
首先,您的 ViewModel
应该像在您的代码中那样公开一些 LiveData
,但应该在其 init
块中启动协程作用域,从端点获取数据.然后当它获取它时,您应该只将 LiveData
值设置为您加载的数据。
在您的 activity 中,与其尝试启动协程来设置您的 RecyclerView
,不如仅使用 LayoutManager
预先设置它,然后开始观察暴露的 [=14] =].然后当被观察的数据发生变化时,才将适配器设置为 RecyclerView
.
快速伪代码概述可能会有所帮助:
ViewModel
ViewModel {
val liveData = MutableLiveData<>()
init {
scope.launch {
liveData.value = api.fetch()
}
}
}
Activity
Activity {
onCreate {
...
recyclerView.layoutManager = LinearLayoutManager
...
viewModel.liveData.observe {
recyclerView.adapter = Adapter(data)
}
}
}
注意: 您也可以使用 liveData(LiveDataScope<T>.() -> Unit)
构建器函数来简化 ViewModel
代码,但我建议您了解这两种方法以及它们有何不同。