Dima Rostopira Dima Rostopira - 1 month ago 15
Android Question

CountDownLatch not freeing thread

I have a method, which loading image from Firebase Storage. It's called in background thread, and I need to block it, until image is loaded (to avoid callback hell). Here is the code (in Kotlin)

override fun fromNet(): Bitmap? {
Log.wtf(TAG, "$name loading from firebase")
var result: Bitmap? = null
val lock = CountDownLatch(1)
try {
FirebaseStorage.getInstance().getReferenceFromUrl(FIRE_STORAGE).child(ctx.getKGL().famkey)
.child(name).getBytes(524288L)
.addOnFailureListener {
Log.wtf(TAG, "$name load failure")
lock.countDown()
}
.addOnSuccessListener { bytes ->
Log.wtf(TAG, "$name loaded")
val b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size).scale(ctx.dip(64))
result = b
lock.countDown()
ctx.saveToCache(name, b)
}
.addOnCompleteListener {
Log.wtf(TAG, "on complete")
lock.countDown()
}
} catch (ignored: NullPointerException) { lock.countDown() }
lock.await()
return result
}


But thread stays blocked forever

Logcat:

A/MemberPhoto: xvd6z67gZfMCLG4c9mkGXKe9ML53 load failure
A/MemberPhoto: on complete


UPD: May the cause be, that Firebase code is Java, and my code is in Kotlin?

Answer

If you want to be sure that lock.await() won't make your current thread wait forever, you need to ensure that lock.countDown() is called whatever happens so here you should surround with a try/finally block the code of your listeners in order to call lock.countDown() within a finally block.

Indeed otherwise with your current code if for example BitmapFactory.decodeByteArray(bytes, 0, bytes.size).scale(ctx.dip(64)) fails, lock.countDown() will never be called which will make the thread calling lock.await() wait forever.

For example in case of a success the code of your listener should rather be:

.addOnSuccessListener { bytes ->
    try {
        Log.wtf(TAG, "$name loaded")
        val b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size).scale(ctx.dip(64))
        result = b
    } finally {
        lock.countDown()
    }        
    ctx.saveToCache(name, b)
}