You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin Android中Firebase Storage顺序上传的错误监听方法

问题

在Kotlin Android应用中,我需要将文件列表上传至Firebase Storage,但并发上传过多文件时会偶发OOM崩溃,因此打算改为顺序上传(每次仅上传一个文件,等待完成后再继续下一个)。

我尝试使用fileRef.putFile(fileUri).await().storage.downloadUrl.await().toString()来等待上传成功后的下载URL,成功时代码运行正常,但上传出错时这段代码会无限等待。

请问:

  • 如何在等待成功结果的同时捕获错误并终止等待?
  • await()是否是实现顺序上传的正确方式?

以下是原异步上传代码(已简化):

val fileUri = Uri.fromFile(file)
val fileRef =
    Firebase.storage.reference.child("$BLAH/$userID/${Uuid}/${fileUri.lastPathSegment}")
fileRef.putFile(fileUri).addOnSuccessListener {
    fileRef.downloadUrl.addOnSuccessListener {
        Firebase.firestore.collection(BLAH).document(BLAH)
            .set(
                blinkybill.toCloud(BLAH, it.toString()),
                SetOptions.merge()
            )
            .addOnSuccessListener {
                updateStatus(CloudSyncStatus.STARTED)
                CoroutineScope(Dispatchers.IO).launch {
                    blahDao.updateBlah(
                        blahEntity.copy(
                            fileUploaded = true
                        )
                    )
                }
            }
            .addOnFailureListener { e ->
                logger("Error uploading the URL to Firebase. Not the one I care about right now")
                updateStatus(CloudSyncStatus.ERROR)
            }
    }
}.addOnFailureListener {
    logger("THIS IS THE UPLOAD ERROR I WANT TO CATCH WHILE WAITING FOR DOWNLOAD")
    updateStatus(CloudSyncStatus.ERROR)
}
解决方案

1. await()是正确的实现方式

Firebase的Task类支持Kotlin协程的await()扩展(需依赖kotlinx-coroutines-play-services库),用它实现顺序上传是合理的,既能避免回调地狱,又天然支持顺序执行逻辑。

2. 捕获错误终止等待的方法

await()在Task执行失败时会抛出ExecutionException(包裹原始错误),只需用**try-catch块**包裹上传逻辑,就能捕获错误并终止等待,不会出现无限挂起的情况。

3. 优化后的顺序上传代码示例

假设你有文件列表fileList,可在协程中遍历列表逐个处理上传:

// 建议使用绑定生命周期的Scope,比如ViewModel的viewModelScope,避免内存泄漏
viewModelScope.launch(Dispatchers.IO) {
    updateStatus(CloudSyncStatus.STARTED)
    fileList.forEachIndexed { index, file ->
        try {
            val fileUri = Uri.fromFile(file)
            val fileRef = Firebase.storage.reference.child("$BLAH/$userID/${Uuid}/${fileUri.lastPathSegment}")
            
            // 上传文件并等待完成
            fileRef.putFile(fileUri).await()
            // 获取下载URL并等待结果
            val downloadUrl = fileRef.downloadUrl.await().toString()
            
            // 更新Firestore(用await替代回调,统一处理错误)
            Firebase.firestore.collection(BLAH).document(BLAH)
                .set(blinkybill.toCloud(BLAH, downloadUrl), SetOptions.merge())
                .await()
            
            // 更新本地数据库
            blahDao.updateBlah(blahEntity.copy(fileUploaded = true))
            logger("第${index+1}个文件上传成功")
            
        } catch (e: Exception) {
            // 捕获所有上传/更新过程中的错误
            when (e) {
                is ExecutionException -> logger("上传失败:${e.cause?.message}")
                else -> logger("未知错误:${e.message}")
            }
            updateStatus(CloudSyncStatus.ERROR)
            // 可选:遇到错误是否终止后续上传?若要终止,直接return
            // return@launch
        }
    }
    // 所有文件上传完成
    updateStatus(CloudSyncStatus.COMPLETED)
}

4. 关键注意事项

  • 依赖配置:确保在build.gradle中添加协程依赖:
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3"
    
  • Scope选择:不要使用GlobalScope,优先用viewModelScope(ViewModel中)或lifecycleScope(Activity/Fragment中),避免内存泄漏。
  • 错误处理粒度:可根据需求拆分try-catch,比如单独捕获上传文件、获取URL、Firestore更新的错误,做精细化处理。
  • 内存优化:顺序上传已避免并发过多导致的OOM,若仍有内存问题,可优化文件读取逻辑,或在每次上传后手动触发垃圾回收(不推荐依赖,仅作临时缓解)。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 05:49:56