为何Firebase Storage异步操作中uriList.add未生效且日志顺序异常?
问题根源
- 异步操作导致顺序异常:Firebase Storage的
listAll()和downloadUrl获取都是异步非阻塞任务,主线程调用这些方法后会立刻执行后续的打印代码,不会等待异步任务完成。所以日志顺序会是firstTmpUri→thirdTmpUri→secondTmpUri,异步回调里的代码要等后台任务跑完才会执行。 - 不是add没生效,是打印时机不对:
uriList.add(task.result)实际是执行了,但你在异步任务还没完成时就打印了uriList,此时添加操作还没执行,所以看到空列表。tmpUrl回到初始值也是同理,异步回调里的赋值操作还没触发,thirdTmpUri就已经输出了初始值。
修复方案(推荐用Kotlin协程)
先确保项目引入Firebase Storage的KTX扩展库(版本20.0.0及以上),把异步操作转为挂起函数,确保所有Uri获取完成后再处理:
private suspend fun getPhotoList() { val fileName = intent.getStringExtra("fileName") ?: return Log.d("fileNameChecking", "$fileName") val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName) var tmpUrl: Uri = Uri.parse(fileName) Log.d("firstTmpUri", "$tmpUrl") try { // 等待listAll任务完成 val listResult = listRef.listAll().await() for (item in listResult.items) { // 等待单个文件的downloadUrl获取完成 val downloadUri = item.downloadUrl.await() tmpUrl = downloadUri Log.d("tmpUri", "$tmpUrl") Log.d("urichecking2", "$downloadUri") uriList.add(downloadUri) } // 所有Uri都获取完成后再打印 Log.d("thirdTmpUri", "$tmpUrl") Log.d("urichecking", "$uriList") } catch (e: Exception) { e.printStackTrace() } }
调用时要放在协程作用域内(比如Activity中):
lifecycleScope.launch { getPhotoList() }
不用协程的替代方案
如果不想用协程,可以用CountDownLatch等待所有下载任务完成:
private fun getPhotoList() { val fileName = intent.getStringExtra("fileName") ?: return Log.d("fileNameChecking", "$fileName") val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName) var tmpUrl: Uri = Uri.parse(fileName) Log.d("firstTmpUri", "$tmpUrl") listRef.listAll() .addOnSuccessListener { listResult -> // 根据文件数量初始化计数器 val latch = CountDownLatch(listResult.items.size) for (item in listResult.items) { item.downloadUrl.addOnCompleteListener { task -> if (task.isSuccessful) { tmpUrl = task.result Log.d("secondTmpUri", "$tmpUrl") Log.d("urichecking2", "${task.result}") uriList.add(task.result) } // 完成一个任务就将计数器减1 latch.countDown() }.addOnFailureListener { it.printStackTrace() latch.countDown() } } // 开启线程等待所有任务完成 Thread { try { latch.await() // 回到主线程执行打印或UI更新操作 runOnUiThread { Log.d("thirdTmpUri", "$tmpUrl") Log.d("urichecking", "$uriList") } } catch (e: InterruptedException) { e.printStackTrace() } }.start() } }
内容的提问来源于stack exchange,提问作者minhyeok
相关产品推荐
相关产品推荐

