英文:
I am getting empty list while fetching data from FirebaseFirestore in Android(Kotlin and jetpack Compose)
问题
以下是您提供的代码的翻译:
我在从Firebase Firestore获取数据时不断获得大小为0的列表,而其中确实有数据,
以下是要查看的代码...
模型...
data class Product(
val productId: String,
val title: String,
val description: String,
val price: Int,
val image: Int?,
val category: String,
val isPopular: Boolean,
)
存储库实现...
override suspend fun getProduct(): Any = withContext(Dispatchers.IO) {
return@withContext try{
val documentReference = firestore
.collection("Products")
.document("")
documentReference
.get()
.addOnSuccessListener { document ->
if (document != null) {
val product = document.toObject(Product::class.java)
Log.d("Successful", "Products received")
} else {
Log.d("ErrorGetting", document.toString())
}
}
.addOnFailureListener {
Log.d("ErrorGetting", it.toString())
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
视图模型...
private val _getProductFlow = MutableStateFlow<List<Product>>(emptyList())
val getProductFlow: StateFlow<List<Product>> = _getProductFlow
suspend fun getProduct() = viewModelScope.launch {
val result = fireRepository.getProduct()
_getProductLiveData.value = listOf(result as Product)
}
}
组合功能...
val pro = viewModel.getProductFlow.collectAsState().value
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(15.dp),
elevation = CardDefaults.cardElevation(
defaultElevation = 4.dp,
hoveredElevation = 8.dp
)
) {
pro.forEach {
Text(text = it.title, color = DarkTeal)
}
}
}
我调试了pro变量,它的大小为0,而我在数据库中存储了3个集合。我也尝试过其他方法,但结果都一样。
此外,这是init块...
init {
CoroutineScope(Dispatchers.IO).launch {
getProduct()
}
}
}
请注意,代码中的一些 HTML 实体字符已被替换为相应的文本字符。如果您需要更多帮助或有其他问题,请随时提出。
英文:
i am constantly getting list of size 0 while fetching data from firebaseFirestore, which have data in it,
here is the code to take a look at...
Model...
data class Product(
val productId: String,
val title: String,
val description: String,
val price: Int,
val image: Int?,
val category: String,
val isPopular: Boolean,
)
Repository implementation..
override suspend fun getProduct(): Any = withContext(Dispatchers.IO) {
return@withContext try{
val documentReference = firestore
.collection("Products")
.document("")
documentReference
.get()
.addOnSuccessListener { document ->
if (document != null) {
val product = document.toObject(Product::class.java)
Log.d("Successful", "Products received")
} else {
Log.d("ErrorGetting", document.toString())
}
}
.addOnFailureListener {
Log.d("ErrorGetting", it.toString())
}
} catch (e: Exception) {
e.printStackTrace()
}
}
ViewModel...
private val _getProductFlow = MutableStateFlow<List<Product>>(emptyList())
val getProductFlow: StateFlow<List<Product>> = _getProductFlow
suspend fun getProduct() = viewModelScope.launch {
val result = fireRepository.getProduct()
_getProductLiveData.value = listOf(result as Product)
}
Composable fun...
val pro = viewModel.getProductFlow.collectAsState().value
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(15.dp),
elevation = CardDefaults.cardElevation(
defaultElevation = 4.dp,
hoveredElevation = 8.dp
)
) {
pro.forEach {
Text(text = it.title, color = DarkTeal)
}
}
I debugged pro variable and it was 0 while i have 3 collections stored in the database. I have tried other ways too but the result is same
also here is the init block...
init {
CoroutineScope(Dispatchers.IO).launch {
getProduct()
}
}
答案1
得分: 1
因为您正在使用将在您的函数已经返回之后返回某些结果的监听器,而您实际上没有返回您检索到的文档。您将其分配给一个变量,但除了记录它之外什么都没做。
在挂起函数中,您需要使用 await()
而不是使用监听器。如果您没有调用阻塞函数,那么您不需要使用 Dispatchers.IO
。
返回值必须要么是null,要么您需要返回其他东西来表示您的错误情况。
请按照以下方式更改此函数:
override suspend fun getProduct(): Any? {
return@withContext try {
val document = firestore
.collection("Products")
.document("")
.get()
.await()
if (document != null) {
Log.d("Successful", "Products received")
} else {
Log.d("ErrorGetting", "Null document returned")
}
document?.toObject(Product::class.java)
} catch (e: Exception) {
Log.d("ErrorGetting", it.toString())
null
}
}
您还在做一些其他错误的事情:
-
在您的视图模型中,
getProduct()
不应该是一个suspend
函数。这意味着它只会在检索到产品之后返回,并且它会直接返回结果。但它只是启动一个协程。它也不应该被命名为 "get",因为它不返回描述的内容。更合适的命名方式可能是 "launchFetchProduct" 等。 -
永远不要使用
CoroutineScope().launch
。这会创建一个协程,当适当时不会被自动取消,从而导致资源和内存泄漏。通常情况下,您应该使用lifecycleScope
或viewModelScope
,或者将其注入到一个类中。如果您确实想要创建一个不应被取消的协程,因为它不会持有引用或使用应该永久保存的资源,或者直到工作肯定完成,那么使用 GlobalScope 是适当的。
实际上,您有这个 getProduct()
函数,只填充了一个 StateFlow。您必须记住从类外调用它,但又必须设法避免多次调用它,以避免多次获取资源。您可以通过使用 flow
构建器和 stateIn
来避免所有这些以及 MutableStateFlow:
val getProductFlow: StateFlow<List<Product>> = flow {
val result = fireRepository.getProduct() as? Product
if (result == null) {
Log.e(TAG, "Could not load product. Null or invalid response")
return@flow
}
emit(listOf(result))
}.stateIn(viewModelScope, emptyList())
希望这些更改对您有所帮助。
英文:
It's because you're using listeners that will return some result in the future after your function has already returned, and you never actually return the document you retrieve. You assigned it to a variable and did nothing but log it.
In a suspend function, you need to use await()
instead of using listeners. You also don't need Dispatchers.IO
if you aren't calling blocking functions, which you are not doing.
The return value has to either be null, or you need to return something else to represent the error cases you have.
Change this function as shown:
override suspend fun getProduct(): Any? {
return@withContext try{
val document = firestore
.collection("Products")
.document("")
.get()
.await()
if (document != null) {
Log.d("Successful", "Products received")
} else {
Log.d("ErrorGetting", "Null document returned")
}
document?.toObject(Product::class.java)
} catch (e: Exception) {
Log.d("ErrorGetting", it.toString())
null
}
}
A couple other things you're doing wrong:
-
getProduct()
in your view model should not be asuspend
function. That implies that it will only return after the product is retrieved, and that it will directly return the result. But it is just launching a coroutine. It should also not be named "get" because it doesn't return what it describes. It would be more appropriate to name it something like "launchFetchProduct". -
Never use
CoroutineScope().launch
. This creates a coroutine that will not be automatically cancelled when appropriate, leading to resource and memory leaks. Usually, you should be using alifecycleScope
orviewModelScope
, or one that you have injected into a class. If you truly want to create a coroutine that should not be cancelled because it's not holding references or using resources that should be held indefinitely or until work is definitely complete, then GlobalScope is appropriate.
It's actually convoluted that you have this getProduct()
function that only fills in a StateFlow. And you have to remember to call it from outside the class, but you somehow need to avoid calling it more than once to avoid wasting resources fetching it multiple times. You could avoid all this and the MutableStateFlow by using the flow
builder and stateIn
:
val getProductFlow: StateFlow<List<Product>> = flow {
val result = fireRepository.getProduct() as? Product
if (result == null) {
Log.e(TAG, "Could not load product. Null or invalid response")
return@flow
}
emit(listOf(result))
}.stateIn(viewModelScope, emptyList())
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论