Android中Firestore请求的ExecutionException嵌套异常处理方案
解决Firestore Tasks.await()中ExecutionException包裹FirebaseFirestoreException的问题
我之前也碰到过这个问题,核心原因是Tasks.await()的设计特性:它会把Task执行过程中抛出的所有异常都包装成ExecutionException,所以你直接捕获FirebaseFirestoreException根本命中不了,得从这个包装类里把原始异常掏出来才行。
怎么处理特定的Firestore错误?
你需要在ExecutionException的捕获分支里,通过e.cause拿到真正的异常,然后判断它是不是FirebaseFirestoreException,再根据Firestore的错误码区分具体场景,给用户展示不同提示。
修改后的完整代码示例
private const val TAG = "FirestoreHelper" suspend fun getUsers() = withContext(Dispatchers.IO) { try { val ref = Firebase.firestore.collection("users") val task = ref.get(Source.SERVER) val snapshot = Tasks.await(task) val users = snapshot.map { it.toObject(User::class.java) } // 成功获取数据,返回或者做后续处理 users } catch (e: Exception) { when (e) { // 这个分支其实很少直接触发,大多是Firestore API同步抛出的异常 is FirebaseFirestoreException -> handleFirestoreError(e) is ExecutionException -> { val realException = e.cause if (realException is FirebaseFirestoreException) { // 这才是你要处理的真正Firestore异常 handleFirestoreError(realException) } else { // 处理非Firestore原因导致的执行异常,比如线程中断、其他IO异常等 Log.i(TAG, "执行出错(非Firestore问题): ${e.localizedMessage}") // 这里可以给用户展示通用错误提示,比如"操作失败,请重试" } } else -> { Log.i(TAG, "未知异常: ${e.localizedMessage}") // 通用异常提示 } } // 异常场景下返回空列表或者null,根据你的业务需求调整 emptyList() } } private fun handleFirestoreError(e: FirebaseFirestoreException) { when(e.code) { FirebaseFirestoreException.Code.UNAVAILABLE -> { // 网络不可用或者服务器无法访问,提示用户检查网络 Log.i(TAG, "网络连接异常,请检查你的网络后重试") // 这里可以通过LiveData/Flow通知界面显示对应的提示 } FirebaseFirestoreException.Code.PERMISSION_DENIED -> { // 权限不足,提示用户没有访问权限 Log.i(TAG, "抱歉,你没有权限查看用户数据") } FirebaseFirestoreException.Code.NOT_FOUND -> { // 请求的集合或文档不存在 Log.i(TAG, "用户数据不存在") } // 还可以根据业务需求添加更多错误码的处理,比如超时、数据格式错误等 else -> { Log.i(TAG, "服务器错误: ${e.localizedMessage}") } } }
怎么区分ExecutionException的不同触发原因?
ExecutionException本质就是个"异常包装器",真正的问题在它的cause属性里:
- 先判断
cause的类型:如果是FirebaseFirestoreException,就用它的code字段区分具体错误类型(网络、权限、资源不存在等) - 如果
cause是其他类型(比如IOException、SecurityException、CancellationException),就根据对应的异常类型处理不同场景,比如线程中断就提示用户操作被取消,IO异常就提示存储问题等
额外小建议
如果你用Firebase的KTX扩展库,其实可以用更贴合协程的API,不用手动调用Tasks.await()——Firebase提供了await()扩展函数,它会直接抛出原始异常,不需要处理包装类,代码会更简洁:
// 先添加依赖到build.gradle: // implementation 'com.google.firebase:firebase-firestore-ktx:你的Firebase版本' // implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:你的协程版本' suspend fun getUsers() = withContext(Dispatchers.IO) { try { val ref = Firebase.firestore.collection("users") val snapshot = ref.get(Source.SERVER).await() val users = snapshot.map { it.toObject(User::class.java) } users } catch (e: FirebaseFirestoreException) { handleFirestoreError(e) emptyList() } catch (e: Exception) { Log.i(TAG, "其他异常: ${e.localizedMessage}") emptyList() } }
这样异常处理逻辑更直观,也少了一层包装的麻烦。
内容的提问来源于stack exchange,提问作者ameencarpenter
相关产品推荐
相关产品推荐

