简体   繁体   中英

Android Firebase cannot download images and add them to a Bitmap Array

I am currently working on a matching card game where I need to store the images on Firebase. I am uploading the images by a button click when I start the game(doing it automatically creates same problem but the button one is safer) I think the Image isn't getting downloaded fast enough to show on the card face or it might not be working in a sequence with the whole app so the bitmap array gets zero elements inside. My current code is:

class game2x2 : AppCompatActivity() {
    private lateinit var database: DatabaseReference
    private lateinit var buttons: List<ImageButton>
    //private lateinit var bitmapArray: ArrayList<Bitmap>
    private var bitmapArray = mutableListOf<Bitmap>()
    private lateinit var button1: ImageButton
    private lateinit var button2: ImageButton
    private lateinit var button3: ImageButton
    private lateinit var button4: ImageButton
    private lateinit var upload: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_game2x2)

        val min = 1
        val max = 45
        val database = FirebaseDatabase.getInstance()
        val imageID1 = Random().nextInt(max - min + 1) + min
        val imageID2 = Random().nextInt(max - min + 1) + min
        val aDatabase = FirebaseStorage.getInstance().getReference("all/$imageID1.jpg")
        val sDatabase = FirebaseStorage.getInstance().getReference("all/$imageID2.jpg")

        upload = findViewById(R.id.uploadButton)
        button1 = findViewById(R.id.imageButton1)
        button2 = findViewById(R.id.imageButton2)
        button3 = findViewById(R.id.imageButton3)
        button4 = findViewById(R.id.imageButton4)
        buttons = listOf(button1, button2, button3, button4)

        upload.setOnClickListener(View.OnClickListener {
            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")
                    Toast.makeText(this, "ERROR RETRIEVING IMAGE", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

            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")
                    Toast.makeText(this, "ERROR RETRIEVING IMAGE", Toast.LENGTH_SHORT).show()
                }
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
            ///    DUPLICATE
            bitmapArray.addAll(bitmapArray)
            ///SHUFFLE
            bitmapArray.shuffle()
            Log.w("myapplication", bitmapArray.size.toString())
        })

        buttons.forEachIndexed { index, button ->
            button.setOnClickListener(View.OnClickListener {
                button.setImageBitmap(bitmapArray[index])
            })
        }
    }
}

Is there any other way to retrieve image from the Firebase Storage besides downloading and adding it to a temporary file and then decoding it to a bitmap?

I tried anything that I could find. I even tried adding the access tokens of the images to a realtime database and then getting them from there but I failed terribly. Thanks in advance for helping!

Since getFile() an asynchronous task I would imagine your log statement Log.w("myapplication", bitmapArray.size.toString()) is executing while the bitmapArray is still empty? This would happen because the aDatabase.getFile().addOnSuccessListener {} and sDatabase.getFile().addOnSuccessListener {} won't execute until the download finishes, but allow the rest of your function to continue to execute.

What you need to do is await the results of the downloads before continuing with the duplicate and shuffle portions.

getFile() returns a FileDownloadTask , which inherits from StorageTask . StorageTask has an isComplete() method -- and a few others the may be useful for errors cases. One option would be to capture the FileDownloadTask in a variable and not continue executing until your downloads are finished. However, be warned this might freeze up your main thread.

Edit: Instead of checking status on the main thread, you might want to try something like disabling the buttons until the images are ready. See edit comments:

class game2x2 : AppCompatActivity() {
    private lateinit var database: DatabaseReference
    private lateinit var buttons: List<ImageButton>
    //private lateinit var bitmapArray: ArrayList<Bitmap>
    private var bitmapArray = mutableListOf<Bitmap>()
    private lateinit var button1: ImageButton
    private lateinit var button2: ImageButton
    private lateinit var button3: ImageButton
    private lateinit var button4: ImageButton

    private val numImages = 2 // EDIT total number of images we need to download
    private val numImagesReady = AtomicInteger(0) // EDIT count of how many images are currently ready

    private lateinit var upload: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_game2x2)

        val min = 1
        val max = 45
        val database = FirebaseDatabase.getInstance()
        val imageID1 = Random().nextInt(max - min + 1) + min
        val imageID2 = Random().nextInt(max - min + 1) + min
        val aDatabase = FirebaseStorage.getInstance().getReference("all/$imageID1.jpg")
        val sDatabase = FirebaseStorage.getInstance().getReference("all/$imageID2.jpg")

        upload = findViewById(R.id.uploadButton)
        button1 = findViewById(R.id.imageButton1)
        button2 = findViewById(R.id.imageButton2)
        button3 = findViewById(R.id.imageButton3)
        button4 = findViewById(R.id.imageButton4)
        buttons = listOf(button1, button2, button3, button4)

        // EDIT disable buttons until all images are ready
        buttons.forEach {
            it.setEnabled(false)
        }

        upload.setOnClickListener(View.OnClickListener {
            try {
                val localfile = File.createTempFile("tempfile", ".jpg")
                aDatabase.getFile(localfile).addOnSuccessListener {
                    val bitmap = BitmapFactory.decodeFile(localfile.absolutePath)
                    bitmapArray.add(bitmap)


                    // EDIT add the image twice here instead of duplicating later
                    bitmapArray.add(bitmap)

                    // EDIT count this image as ready
                    val totalImagesReady = numImagesReady.incrementAndGet()
                    
                    // EDIT once all images are ready, shuffle and enable the buttons
                    if (totalImagesReady == numImages) {
                        bitmapArray.shuffle()
                        buttons.forEach { it.setEnabled(true) }
                    }


                }.addOnFailureListener {
                    Log.w("myapplication", "ERROR RETRIEVING IMAGE")
                    Toast.makeText(this, "ERROR RETRIEVING IMAGE", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

            try {
                // SUGGESTION especially if this will be implemented 8x8, you might want to try implementing this in a loop instead of duplicating code
                val localfile = File.createTempFile("tempfile1", ".jpg")
                sDatabase.getFile(localfile).addOnSuccessListener {
                    val bitmap = BitmapFactory.decodeFile(localfile.absolutePath)
                    bitmapArray.add(bitmap)

                    // EDIT add the image twice here instead of duplicating later
                    bitmapArray.add(bitmap)

                    // EDIT count this image as ready
                    val totalImagesReady = numImagesReady.incrementAndGet()
                    
                    // EDIT once all images are ready, shuffle and enable the buttons
                    if (totalImagesReady == numImages) {
                        bitmapArray.shuffle()
                        buttons.forEach { it.setEnabled(true) }
                    }


                }.addOnFailureListener {
                    Log.w("myapplication", "ERROR RETRIEVING IMAGE")
                    Toast.makeText(this, "ERROR RETRIEVING IMAGE", Toast.LENGTH_SHORT).show()
                }
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
// EDIT moved            ///    DUPLICATE
// EDIT refactor         bitmapArray.addAll(bitmapArray)
// EDIT moved            ///SHUFFLE
// EDIT moved            bitmapArray.shuffle()
// EDIT remove           Log.w("myapplication", bitmapArray.size.toString())
        })

        buttons.forEachIndexed { index, button ->
            button.setOnClickListener(View.OnClickListener {
                button.setImageBitmap(bitmapArray[index])
            })
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM