简体   繁体   English

为什么我的 CoroutineScope 在它有 Firebase 数据并完成之前不停止代码?

[英]Why doesnt my CoroutineScope stop the code until it has the Firebase data and is finished?

I created a CoroutineScope to get the data from Firebase before expanding a Card and showing that data in a listView .我创建了一个CoroutineScope以从 Firebase 获取数据,然后展开 Card 并在listView中显示该数据。 But the card expands while the CoroutineScope is still getting the data from Firebase and tries to display the listView with an empty list.但是当CoroutineScope仍在从 Firebase 获取数据并尝试显示带有空列表的listView时,卡片会扩展。

Here is my Expand Function inside the OnClickListener ( StartPageActivity.customClassList is a List in a Object that is defined earlier):这是我在 OnClickListener 中展开的OnClickListenerStartPageActivity.customClassList是前面定义的 Object 中的一个List ):

        var list : List<CustomClass> = ArrayList()

        CoroutineScope(Dispatchers.IO).launch {
            var customList: List<CustomClass> = ArrayList()
            StartPageActivity.customClassExpandList.forEach {
                if (it.title.contains(CustomClass.title)) {
                    customList += getFirebaseData(it.date)
                    if (customList.size == 12) {
                        list = customList
                    }
                }
            }
        }

        val listAdapter = MyListAdapter(context, list)

        listView.adapter = listAdapter

        listView.visibility = View.VISIBLE
        listView.dividerHeight = 0
        listView.layoutParams.height = calculateHeight(listView, 12) 

Here is my getFirebaseData function:这是我的getFirebaseData function:

suspend fun getFirebaseDate(date : LocalDate) : CustomClass = withContext(Dispatchers.IO){
    val customClass = CustomClass("$date", date, "Empty", false)
             FirebaseFirestore.getInstance().collection("users").document(FirebaseAuth.getInstance().currentUser!!.uid).collection("customClass")
        .document(date.toString()).get().addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val document = task.result
                if (document.exists()) {
                    goal.description = document["description"].toString()
                    goal.title = document["tile"].toString()
                }
            }
        }.await()
    return@withContext customClass
}

The getFirebaseData function works and it returns the customClass ; getFirebaseData function 有效并返回customClass this is also added to my customList .这也被添加到我的customList中。 But this happens while the code tries to build the expanded listView with the list, that is initiated before the CoroutineScope .但这发生在代码尝试使用列表构建扩展的listView时,该列表在CoroutineScope之前启动。

I tried to run the Code after the CoroutineScope inside that scope, but it doesn't allow that and returns an error.我试图在CoroutineScope内的 CoroutineScope 之后运行代码,但它不允许这样做并返回错误。

I also tried adding multiple suspend functions, but that also has not fixed my problem.我也尝试添加多个暂停功能,但这也没有解决我的问题。

I also tried putting the forEach function in a separate suspend function, but my problem still occurred.我还尝试将forEach function 放在单独的挂起 function 中,但我的问题仍然出现。

Launching a coroutine queues it up to run in parallel or in the background, and the current function continues running without waiting for it.启动协程会将其排队等待并行运行或在后台运行,当前 function 继续运行而无需等待。 See here for more explanation.有关更多说明,请参见此处。

To get the behavior you're expecting, you would need to use runBlocking instead of CoroutineScope(Dispatchers.IO).launch .要获得您期望的行为,您需要使用runBlocking而不是CoroutineScope(Dispatchers.IO).launch However, this is not a viable solution for a.network operation.然而,这不是网络操作的可行解决方案。 The entire device will be completely frozen to user input and screen refreshes/animation until the coroutine is finished.整个设备将完全冻结用户输入和屏幕刷新/动画,直到协程完成。 If the.network operation takes more than a few milliseconds, this will be unacceptable to the user, because they can't even back out of your app if they change their mind, among many other potential problems.如果.network 操作花费的时间超过几毫秒,这将是用户无法接受的,因为如果他们改变主意,他们甚至无法退出您的应用程序,还有许多其他潜在问题。

You must design your app to gracefully handle the fact that a.network operation might take a while.您必须设计您的应用程序以优雅地处理网络操作可能需要一段时间的事实。 This is typically done by showing some kind of loading indicator in the UI, and then you can start your coroutine, and then inside the coroutine you can update the list view when the results are ready.这通常是通过在 UI 中显示某种加载指示器来完成的,然后您可以启动协程,然后在协程内您可以在结果准备就绪时更新列表视图。

Two other issues with your use of coroutines:使用协程的另外两个问题:

  1. You should not create a new CoroutineScope like that.你不应该像那样创建一个新的 CoroutineScope。 It will leak your fragment.它会泄漏你的片段。 Use viewLifecycleOwner.lifecycleScope.launch .使用viewLifecycleOwner.lifecycleScope.launch And don't use Dispatchers.IO , because that will cause glitches and crashes if you start working with UI in your coroutine.并且不要使用Dispatchers.IO ,因为如果您开始在协程中使用 UI,这会导致故障和崩溃。 You don't need Dispatchers.IO unless you're directly calling blocking code inside your coroutine, and if you do, you should wrap only those parts of the coroutine with withContext(Dispatchers.IO) { } .你不需要Dispatchers.IO除非你直接在你的协程中调用阻塞代码,如果你这样做,你应该只用withContext(Dispatchers.IO) { }包装协程的那些部分。

  2. In your suspend function, you should not mix using listeners and await() .在您的暂停 function 中,您不应混合使用侦听器和await() That is convoluted and error prone.这是令人费解且容易出错的。 You're also neglecting the failure condition.您还忽略了失败条件。 If you don't use try / catch when using await() , you risk a crash or unhandled error (depending on how your coroutine scope is set up).如果在使用await()时不使用try / catch ,则可能会发生崩溃或未处理的错误(取决于协程 scope 的设置方式)。 Incidentally, you don't need to specify Dispatchers.IO here (like I mentioned above) because await() is not a blocking function. It's a suspending function. Your function should look something like this instead.顺便说一句,您不需要在此处指定Dispatchers.IO (就像我上面提到的那样),因为await()不是阻塞 function。它是一个暂停的 function。您的 function 应该看起来像这样。 I'm guessing that you are using CustomClass to communicate whether the retrieval was successful.我猜您正在使用 CustomClass 来传达检索是否成功。

suspend fun getFirebaseDate(date : LocalDate) : CustomClass = try {
    val document = FirebaseFirestore.getInstance()
        .collection("users")
        .document(FirebaseAuth.getInstance().currentUser!!.uid)
        .collection("customClass")
        .document(date.toString())
        .await()
    require(document.exists()) // Throw exception if it doesn't. Catch block will handle it.
    goal.description = document["description"].toString()
    goal.title = document["tile"].toString()
    CustomClass("$date", date, "Empty", false)
} catch (e: Exception) {
    CustomClass("$date", date, "Empty", true)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我的 Firebase Firestore 数据未显示在 android 中 - Why my Firebase firestore data is not showing in android 为什么我的图像数据 url 没有保存在 firebase 火库中? - Why my image's data url is not saving in firebase firestore? 为什么我的 Firebase Cloud Firestore 数据文档随机显示? - Why my Firebase Cloud Firestore data document randomly showing? 为什么我的 Firebase 数据库没有按照 Model class 存储数据? - Why My Firebase database not storing data according to Model class? Firebase 中的异步 function 甚至在 function 主体完成执行之前就解析了 promise - Async function in Firebase resolves the promise even before the function body has finished executing Firebase 实时数据库,我怎么知道数据集何时完成加载 - Firebase Realtime Database, how do I know when dataset has finished loading 为什么我的 React Native firebase 登录不起作用? - Why is my react native firebase login not working? 当我的 typescript function 完成时停止 Fargate 任务 - Stop Fargate Task when my typescript function has completed 为什么我的图像没有从 firebase 火库中显示? - Why my images is not showing from firebase firestore? 为什么我的 Firebase 数据库的某些属性未定义? - Why are some properties of my Firebase database undefined?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM