如何解决使用Room时如何一次获得查询结果?
我希望一次通过Room数据库获取所有记录的总数。但是,通常Room使用后台线程来异步查询记录。
如果我在代码A中使用getTotalOfVoiceAsLiveData()
,它将返回LiveData<Long>,
,您知道LiveData变量是惰性的,也许结果为空。
如果我在代码A中使用getTotalOfVoice()
,则会因我无法在return
中使用viewModelScope.launch{ }
而出错。
如何通过Room数据库一次获取所有记录的总数?
代码A
class HomeViewModel(val mApplication: Application,private val mDBVoiceRepository: DBVoiceRepository) : AndroidViewModel(mApplication) {
fun getTotalOfVoice():Long {
viewModelScope.launch {
return mDBVoiceRepository.getTotalOfVoice() //It will cause error
}
}
fun getTotalOfVoiceAsLiveData(): LiveData<Long>{
return mDBVoiceRepository.getTotalOfVoiceAsLiveData() //It's lazy,maybe the result is null.
}
}
class DBVoiceRepository private constructor(private val mDBVoiceDao: DBVoiceDao){
suspend fun getTotalOfVoice() = mDBVoiceDao.getTotalOfVoice()
fun getTotalOfVoiceAsLiveData() = mDBVoiceDao.getTotalOfVoiceAsLiveData()
}
@Dao
interface DBVoiceDao{
@Query("SELECT count(id) FROM voice_table")
suspend fun getTotalOfVoice(): Long
//When Room queries return LiveData,the queries are automatically run asynchronously on a background thread.
@Query("SELECT count(id) FROM voice_table")
fun getTotalOfVoiceAsLiveData(): LiveData<Long>
}
添加内容
Tobi:谢谢!
为什么直接获取数据对您来说很重要?
我需要基于记录的总数生成文件名,例如“ untitled0”,“ untitled1”,“ untitled2” ...
如果我可以立即获得查询结果,则可以轻松使用以下代码。
再次添加
当我单击开始按钮时,希望根据查询记录的总数按文件名记录语音。您知道添加或删除reocrd时记录总数将发生变化!
代码B
fun getTotalOfVoice():Long {
//Get the query result at once
...
}
fun createdFileanme(){
return "untitled"+getTotalOfVoice().toString()
}
btnStart.setOnClickListener{
recordVoice(createdFileanme()) //I will record voice by filename
}
fun addRecord(){
...
}
fun deleteRecord(){
...
}
新添加的内容
谢谢!
我认为'You should also move all of that into the viewmodel class,without LiveData '
是个好方法,您可以看到图片A和How can I get the value of a LivaData<String> at once in Android Studio?。
您同意吗?
解决方法
问题:立即意味着同步还是什么?如果是,如果获得结果的功能需要花费更长的时间怎么办?喜欢网络通话?好,您可以决定在另一个线程上执行该操作。
我认为您可以使用可变对象并使用postValue函数将结果分发给观察者。它应如下所示:
class HomeViewModel(val mApplication: Application,private val mDBVoiceRepository: DBVoiceRepository) : AndroidViewModel(mApplication) {
private val voices = MutableLiveData<Long>()
fun getTotalOfVoiceAsLiveData(): LiveData<Long> {
voices.postValue(mDBVoiceRepository.getTotalOfVoiceAsLiveData().value)
return voices;
}
}
在片段中使用它的方式如下所示:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (activity != null) {
val viewModel = ViewModelProvider(requireActivity())
viewModel.get(HomeViewModel::class.java).getTotalOfVoiceAsLiveData().observe(viewLifecycleOwner,Observer { voices: Long ? ->
voices // Sound of music ? be very free to use ...
})
}
}
快乐编码。
,您可以从Deferred<Long>
返回viewModelScope.async
。我建议您使用:
val deferred = viewModelScope.async(Dispatchers.IO) {
return@async mDBVoiceRepository.getTotalOfVoice()
}
val value = deferred.await()
await()已暂停
编辑: 如果您想获取将在您的活动或片段中使用的吸气剂 您需要编写这样的暂停函数:
suspend fun getTotalOfVoice(): Long {
return viewModelScope.async(Dispatchers.IO) {
return@async mDBVoiceRepository.getTotalOfVoice()
}.await()
}
但是mvvm模式允许您在ViewModel内创建LiveData,这为您的片段提供了一个观察者。
在视图模型中:
private val _totalOfVoiceLD: MutableLiveData<Long> = MutableLiveData()
val totalOfVoiceLD: LiveData<Long>
get() = _totalOfVoiceLD
fun updateTotalOfVoice() {
viewModelScope.launch(Dispatchers.IO) {
val totalOfVoice = mDBVoiceRepository.getTotalOfVoice()
_totalOfVoiceLD.postValue(totalOfVoice)
}
}
以及您的片段中
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
viewModel.totalOfVoiceLD.observe(viewLifecycleOwner,Observer { totalOfVoice ->
totalOfVoiceTextView.text = totalOfVoice.toString()
})
}
,
您可以使用 coroutineContext.async 从数据库获取数据,并通过使用 .await 函数进行异步调度来等待其对数据的响应。
suspend fun getAllVoices() : Long{
val awatingResults = viewModelScope.async(Dispatchers.IO) {
mDBVoiceRepository.getTotalOfVoice()
}
val records = awatingResults.await()
return records
}
有必要从协程调用Suspend函数,然后 始终在已暂停的函数中调用 async.await(),
val voiceLiveData: MutableLiveData<Long> = MutableLiveData()
fun getAllVoicesFromDB() {
viewModelScope.launch(Dispatchers.IO) {
voiceLiveData.postValue(mDBVoiceRepository.getTotalOfVoice())
}
}
现在在您要从数据库中获取语音数据的任何地方调用它,还记得在 voiceLiveData 观察者中进行进一步的工作:在此您可以得到语音的响应: )
,如果您同步需要Room结果,则您的代码应在IO线程中执行。如果是协程,则可以使用Dispatchers.IO
。您的代码可以更改为此,以传递错误。
class HomeViewModel(val mApplication: Application,private val mDBVoiceRepository: DBVoiceRepository) : AndroidViewModel(mApplication) {
fun getTotalOfVoice():Long {
viewModelScope.launch(Dispatchers.IO) { // here
return mDBVoiceRepository.getTotalOfVoice()
}
}
}
,
我希望立即得到结果,但是LiveData很懒惰
很抱歉,但这是Room接口设计的方式。 您对返回的LiveData对象的懒惰是正确的。但这使您可以在不同的线程上处理它,而不必手动处理不同的线程。
根据您的新信息!
您基本上有两个选择:
A),您可以执行以下操作:
- 通过LivaData从Room加载数据
- 添加存储当前总金额的观察者
- 单击按钮时,您只需阅读本地副本
在您的视图中:(只有一名观察者和一名clickListener)
val totalVoiceCount: long
val viewModel = ViewModelProvider(requireActivity()).get(HomeViewModel::class.java)
viewModel.getTotalOfVoiceAsLiveData().observe(viewLifecycleOwner,Observer { totalOfVoice : Long ? ->
if (totalOfVoice != null)
totalVoiceCount = totalOfVoice
})
btnStart.setOnClickListener{
viewModel.recordVoice(totalVoiceCount)
}
在您的ViewModel中:(逻辑和其他所有内容)
fun recordVoice(totalVoiceCount : long){
val fileName = createdFileanme(totalVoiceCount)
// create your recording // depending on how you do this,it probably runs on a background thread anyways
}
fun createdFileName(totalVoiceCount : long){
return "untitled"+ String.valueOf(totalVoiceCount)
}
之所以能够可靠地工作,是因为LiveData在用户有机会单击按钮之前有足够的时间来更新totalVoiceCount的本地副本。
B)当然,基于您的并行问题的答案,您甚至可以将更多资源外包给后台线程。然后,您还可以选择使用非实时数据返回调用DAO查询(因为会议室仅在后台线程上返回非实时数据查询)。实施Ridcully的穿线建议是否值得?在不知道其他情况同时发生的情况下无法回答。在我看来,这似乎有些过头了,但他说得对,您对后台线程执行的操作越多,刷新率就越好。
,实时数据被设计为惰性的,当实时数据的值在内部发生变化时,它会发出该信号,并且无论您在何处观察它,都将调用onChange函数。它旨在激发并忘记。 因为room使用后台线程来运行查询。 您不能指望实时数据的行为类似于存储键值对的sharedpreference。 如果您想实现这样的目标。 我建议你用 纸Db或领域。
,如果必须在主线程中运行查询,则:
-
允许android空间在主线程中执行查询。
val dbInstance = Room .databaseBuilder(ctx,YourDBClass::class.java,"YourDBName") .allowMainThreadQueries() .build()
-
定义dao方法如下
@Dao interface DBVoiceDao{ @Query("SELECT count(id) FROM voice_table") fun getTotalOfVoice(): Long }
-
访问存储库中的方法
fun getTotalOfVoice():Long { return dao.getTotalOfVoice() }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。