[英]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 中展开的
OnClickListener
( StartPageActivity.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:使用协程的另外两个问题:
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) { }
包装协程的那些部分。
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.