Android Firebase无法下载图片至Bitmap数组及替代方案咨询
问题1:Bitmap数组为空的原因及解决方法
核心原因:异步执行顺序错误
你的代码中,bitmapArray.addAll(bitmapArray)、bitmapArray.shuffle()以及日志打印操作,都是在图片异步下载完成前执行的。getFile()是异步方法,addOnSuccessListener中的代码只有在图片下载完成后才会回调执行,此时bitmapArray还未添加任何元素,执行addAll后依然是空数组。
修复方案:等待所有异步任务完成后再处理数组
使用Firebase的Tasks.whenAll()合并两个下载任务,等两张图片都下载完成后,再执行数组复制、打乱操作:
upload.setOnClickListener(View.OnClickListener { // 每次点击清空数组,避免重复累积元素 bitmapArray.clear() val task1 = try { val localfile = File.createTempFile("tempfile", ".jpg") aDatabase.getFile(localfile).addOnSuccessListener { val bitmap = BitmapFactory.decodeFile(localfile.absolutePath) bitmapArray.add(bitmap) }.addOnFailureListener { Log.w("myapplication", "ERROR RETRIEVING IMAGE 1") Toast.makeText(this, "ERROR RETRIEVING IMAGE 1", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { e.printStackTrace() null } val task2 = try { val localfile = File.createTempFile("tempfile1", ".jpg") sDatabase.getFile(localfile).addOnSuccessListener { val bitmap = BitmapFactory.decodeFile(localfile.absolutePath) bitmapArray.add(bitmap) }.addOnFailureListener { Log.w("myapplication", "ERROR RETRIEVING IMAGE 2") Toast.makeText(this, "ERROR RETRIEVING IMAGE 2", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { e.printStackTrace() null } // 等待两个下载任务都完成 Tasks.whenAll(task1, task2).addOnSuccessListener { // 复制数组并打乱 bitmapArray.addAll(bitmapArray) bitmapArray.shuffle() Log.w("myapplication", bitmapArray.size.toString()) // 此时输出应为4 // 重置按钮点击逻辑,确保数组可用时再操作 buttons.forEachIndexed { index, button -> button.tag = false // 标记卡片是否已翻转 button.setOnClickListener { val isFlipped = button.tag as Boolean if (!isFlipped && bitmapArray.isNotEmpty()) { button.setImageBitmap(bitmapArray[index]) button.tag = true } else { // 切换回卡片背面图,需替换为你的背面资源ID button.setImageResource(R.drawable.card_back) button.tag = false } } } }.addOnFailureListener { Log.w("myapplication", "One or more download tasks failed") } })
问题2:Firebase Storage获取图片的其他方法
除了下载到临时文件再解码,还有以下三种常用方式:
1. 直接获取字节数组解码为Bitmap
跳过临时文件,直接获取图片字节数组并解码:
// 设置最大下载字节数,示例为10MB val maxSize = 10L * 1024 * 1024 aDatabase.getBytes(maxSize).addOnSuccessListener { bytes -> val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) // 使用bitmap更新UI或存入数组 }.addOnFailureListener { Log.w("myapplication", "Failed to get image bytes") }
2. 使用图片加载库(推荐)
用Glide或Picasso这类成熟库,自动处理异步加载、缓存和内存管理,直接加载Firebase Storage引用:
Glide示例(需先添加依赖)
// 模块级build.gradle添加依赖 // implementation 'com.github.bumptech.glide:glide:4.16.0' // annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' Glide.with(this) .load(aDatabase) // 直接传入FirebaseStorage的Reference .into(button1) // 加载到目标ImageButton
Picasso示例
// 添加依赖:implementation 'com.squareup.picasso:picasso:2.71828' Picasso.get() .load(aDatabase) .into(button1)
3. 获取下载URL后加载
如果需要将URL存入实时数据库,正确的获取和使用方式如下:
获取并存储下载URL
aDatabase.downloadUrl.addOnSuccessListener { uri -> val fullImageUrl = uri.toString() // 将URL存入实时数据库 FirebaseDatabase.getInstance().getReference("gameImages/image1").setValue(fullImageUrl) }.addOnFailureListener { Log.w("myapplication", "Failed to get download URL") }
从数据库读取URL并加载
FirebaseDatabase.getInstance().getReference("gameImages/image1") .addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val imageUrl = snapshot.getValue(String::class.java) ?: return // 用Glide加载 Glide.with(this@game2x2) .load(imageUrl) .into(button1) // 或手动解码(需在子线程执行) Thread { val bitmap = BitmapFactory.decodeStream(URL(imageUrl).openStream()) runOnUiThread { button1.setImageBitmap(bitmap) } }.start() } override fun onCancelled(error: DatabaseError) { Log.w("myapplication", "Failed to read image URL from database") } })
你之前尝试存访问令牌失败,大概率是没有获取完整的下载URL,而是只存储了令牌片段。通过downloadUrl获取的URI包含完整的访问参数,可直接用于加载图片。
内容的提问来源于stack exchange,提问作者Berk Sunduri
相关产品推荐
相关产品推荐

