Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android:await()似乎无法使用Room数据库_Android_Kotlin_Android Room_Kotlin Coroutines_Android Database - Fatal编程技术网

Android:await()似乎无法使用Room数据库

Android:await()似乎无法使用Room数据库,android,kotlin,android-room,kotlin-coroutines,android-database,Android,Kotlin,Android Room,Kotlin Coroutines,Android Database,我正在开发一个应用程序来练习一些计算,它将每个给定的答案和有关练习课程的数据保存到房间数据库中,以便跟踪进度。有一个表包含答案,有一个表包含会话,答案表中的每一行都需要包含给出答案的会话的id 我使用的是一个房间数据库,因此在写入数据库时使用了协程 当用户单击按钮时,将评估并保存答案,同时更新会话数据。 (如回答的问题数量和平均分数。) 为了实现这一点,我需要新创建的会话数据的Id。因此,我正在尝试调用ViewModel的init{}块中的一个方法,该块使用deferred和call await

我正在开发一个应用程序来练习一些计算,它将每个给定的答案和有关练习课程的数据保存到房间数据库中,以便跟踪进度。有一个表包含答案,有一个表包含会话,答案表中的每一行都需要包含给出答案的会话的id

我使用的是一个房间数据库,因此在写入数据库时使用了协程

当用户单击按钮时,将评估并保存答案,同时更新会话数据。 (如回答的问题数量和平均分数。)

为了实现这一点,我需要新创建的会话数据的Id。因此,我正在尝试调用ViewModel的
init{}
块中的一个方法,该块使用deferred和call await()要等待会话数据被插入,然后从数据库中获取最后一个条目,并更新视图模型保存的SessionData实例,只有当所有操作完成后,我才启用按钮,因此在知道当前会话id之前,我们不会尝试保存任何数据

为了测试这一点,我使用Log.d()方法打印当前会话id。 问题是我并不总是得到正确的值。有时我会得到与前一个会话相同的id,有时我会得到正确的id(所以Android Studio中的Logcat看起来像:33,33,35,36,38,38,40,41,42等等)。但是,如果我从数据库中获取所有数据并将其签出,则所有ID都在数据库中,按照正确的顺序,不会跳过任何值

对我来说,wait()似乎并没有让应用程序真正等待,在我看来,数据库的读取有时发生在写入完成之前

但我不知道为什么

在ViewModel类中:

    
SimpleConversionFragmentViewModel(
    val conversionProperties: ConversionProperties,
    val databaseDao: ConversionTaskDatabaseDao,
    application: Application) : AndroidViewModel(application){ 
  
     private var viewModelJob = Job()  
     private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
     private lateinit var sessionData : SessionData

     ...
 
     init{
         startNewSession()  
     }

     ...

    /**
     *  This method starts and gets the current session
     */
    private fun startNewSession() {
        uiScope.launch {
            /** Initialize session data*/
            sessionData = SessionData() 
            sessionData.taskCategory = conversionProperties.taskCategory
            sessionData.taskType = conversionProperties.taskType

            /**
             *  First insert the new session wait for it to be inserted and get the session inserted
             *  because we need it's ID
             **/
            val createNewSession = async { saveSessionDataSuspend() }
            val getCurrentSessionData = async { getCurrentSessionSuspend() }

            var new = createNewSession.await()
            sessionData = getCurrentSessionData.await() ?: SessionData()
            _isButtonEnabled.value = true //Only let user click when session id is received!!
            Log.d("Coroutine", "${sessionData.sessionId}")
        }
    }
    
    /**
     *  The suspend function to get the current session
     */
    private suspend fun getCurrentSessionSuspend() : SessionData? {
        return withContext(Dispatchers.IO){
            var data = databaseDao.getLastSession()
            data
       }
    }

   /**
     * The suspend function for saving session data
     */
    private suspend fun saveSessionDataSuspend() : Boolean{
        return withContext(Dispatchers.IO) {
            databaseDao.insertSession(sessionData)
            true
        }
    } 

    override fun onCleared() {
        super.onCleared()
        viewModelJob.cancel()
    }
}
下面是ConversionTaskDatabaseDao类的一些详细信息:

@Dao
interface ConversionTaskDatabaseDao {

    @Insert(entity = SessionData::class)
    fun insertSession(session: SessionData)

    @Query("SELECT * FROM session_data_table ORDER BY session_id DESC LIMIT 1")
    fun getLastSession() : SessionData?
    
    ...

}
有人知道怎么解决这个问题吗

我的第一次尝试实际上是在ViewModel的
onCleared()
方法中只保存一次会话数据,但是因为我必须调用viewModelJob.cancel()方法以防止内存泄漏,所以在保存之前会取消该作业。但我认为如果我能在这里只保存一次数据,那将是一种更有效的方法

还是有更好的方法来实现我的目标

提前感谢您的帮助,
最好的问候:Agoston

我的想法是,由于您需要等待一个挂起方法来等待另一个挂起方法,并且它们已经在协同程序(由launch builder启动)中,您不需要等待,您可以简化此过程:

val createNewSession = async { saveSessionDataSuspend() }
val getCurrentSessionData = async { getCurrentSessionSuspend() }

var new = createNewSession.await()
sessionData = getCurrentSessionData.await() ?: SessionData()
为此:

var new = saveSessionDataSuspend()
sessionData = getCurrentSessionSuspend()
相反,当您使用
wait
时,您无法保证首先使用哪种方法