I'm implementing an AsyncTask
in Kotlin, and I need a WeakReference
for the callback that runs in the onPostExecute()
method. I set the listener reference before calling execute()
, but once onPostExecute()
is called, the value of WeakReference
is null
.
class PhotoRotationTask(uri: Uri, filePath: String, resolver: ContentResolver) : AsyncTask<Int, Int, Int>() {
private var weakRef : WeakReference<OnBitmapProcessedListener>? = null
var sourceUri : Uri
var resolver : ContentResolver
var destPath: String
init {
this.sourceUri = uri
this.resolver = resolver
this.destPath = filePath
}
fun setOnBitmapProcessedListener(listener: OnBitmapProcessedListener){
weakRef = WeakReference(listener)
Log.d("RotationTask", "set listener ${weakRef?.get() != null}") //This Log proves that weakRef is initialized before onPostExecute()
}
override fun doInBackground(vararg params: Int?): Int? {
//Bitmap processing, weakRef is never called in this function
}
override fun onPostExecute(result: Int?) {
Log.d("RotationTask", "result: $result") //This log proves that onPostExecute() is called eventually
weakRef!!.get()?.onBitmapProcessed() //This implies that weakRef is not null, because app never crashes, but onBitmapProcessed is not called, so the reference is gone.
}
}
The listener
variable modifies my activity's UI, therefore it holds a reference to my activity. Activity is never recreated, my phone is still, never rotated or touched after AsyncTask starts. How is the WeakReference
cleared??
The problem is in the WeakReference
and local variable that you pass as listener
.
WeakReference
is known not to keep an object from being garbage collected, so if there's no other reachable strong reference to it, it may be recycled at any moment once the method referencing it through local variable finishes. And this is exactly what happens in your case since the weak reference becomes null
.
The solution is to store a strong reference to the object that is passed as listener
somewhere in the calling code (as it uses the activity, the activity itself may store it in a property, so that the listener
's lifetime would match that of the activity).
For example, declare a property
lateinit var currentListener: OnBitmapProcessedListener
in the activity code, then store the listener
you create in that property:
val task = PhotoRotationTask(uri, filePath, resolver)
task.setOnBitmapProcessedListener(object : OnBitmapProcessedListener {
// here goes the implementation
}.apply { currentListener = this } // note this line
)
If multiple tasks and listeners are possible, then take care of storing all the listeners.
You need to hold a strong reference to the OnBitmapProcessedListener somewhere else to ensure the GC doesn't clear WeakReference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With