使用Room时如何一次获得查询结果?

如何解决使用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?

您同意吗?

图片A

enter image description here

解决方法

问题:立即意味着同步还是什么?如果是,如果获得结果的功能需要花费更长的时间怎么办?喜欢网络通话?好,您可以决定在另一个线程上执行该操作。

我认为您可以使用可变对象并使用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),您可以执行以下操作:

  1. 通过LivaData从Room加载数据
  2. 添加存储当前总金额的观察者
  3. 单击按钮时,您只需阅读本地副本

在您的视图中:(只有一名观察者和一名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或领域。

,

如果必须在主线程中运行查询,则:

  1. 允许android空间在主线程中执行查询。

     val dbInstance = Room
         .databaseBuilder(ctx,YourDBClass::class.java,"YourDBName")
         .allowMainThreadQueries()
         .build()
    
  2. 定义dao方法如下

    @Dao
    interface DBVoiceDao{
       @Query("SELECT count(id) FROM voice_table")
       fun getTotalOfVoice(): Long
    }
    
  3. 访问存储库中的方法

    fun getTotalOfVoice():Long {
        return dao.getTotalOfVoice()        
    }
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-