Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WeakReference not working in Kotlin

Tags:

android

kotlin

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??

like image 956
gesuwall Avatar asked May 04 '16 23:05

gesuwall


2 Answers

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.

like image 69
hotkey Avatar answered Oct 15 '22 06:10

hotkey


You need to hold a strong reference to the OnBitmapProcessedListener somewhere else to ensure the GC doesn't clear WeakReference.

like image 42
Mike Buhot Avatar answered Oct 15 '22 04:10

Mike Buhot