在 Kotlin 中 WeakReference 不起作用

20

我正在Kotlin中实现一个AsyncTask,并且我需要在onPostExecute()方法中运行的回调中使用WeakReference。在调用execute()之前,我设置了侦听器引用,但是一旦调用onPostExecute()WeakReference的值就会变为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.
    }

}

listener变量修改了我的活动UI,因此它持有对我的活动的引用。活动永远不会被重新创建,我的手机在AsyncTask启动后仍然静止,从未旋转或触摸。那么WeakReference如何清除?


你是否将本地变量作为“listener”传递?它是否存储在其他地方,例如Activity的属性中? - hotkey
是的,我传递了一个本地变量,但我没有将其存储在任何地方。 - gesuwall
2个回答

20
问题出在你传递作为监听器的局部变量和WeakReference中。 已知WeakReference不能防止对象被垃圾回收, 因此如果没有其他可到达的强引用,一旦通过局部变量引用它的方法完成,它可能在任何时候被回收。这正是你的情况,因为弱引用变成了null
解决方案是在调用代码中存储作为listener传递的对象的强引用(由于使用活动,活动本身可以将其存储在属性中,以便listener的生命周期与活动的生命周期相匹配)。
例如,声明一个属性
lateinit var currentListener: OnBitmapProcessedListener

在活动代码中,然后将您创建的 listener 存储在该属性中:
val task = PhotoRotationTask(uri, filePath, resolver)

task.setOnBitmapProcessedListener(object : OnBitmapProcessedListener {
         // here goes the implementation
     }.apply { currentListener = this } // note this line
)

如果可能存在多个任务和监听器,则需要注意存储所有的监听器。

3
当需要存储一个强引用时,为什么要使用弱引用?这样做不会阻止对象被垃圾回收吗?(即弱引用的目的是允许包含的对象在需要时可自由释放)。 - Elye
我不是专家,但如果我必须持有一个强引用来正确使用同一对象的弱引用,为什么不只是拥有一个手动可空对象,这样我就可以决定何时可以清除引用。 - rgv
通常,监听器接口由活动类实现,并将活动实例传递给异步任务。系统会保留活动的引用 - 至少在它没有被销毁的时间内。通过将其作为WeakReference保存在异步任务中,您不会冒泄漏活动引用的风险。根据操作员的描述,监听器是使用单独的类实现的,该类保留对Activity的引用 - 至少从原始描述中不清楚。 - marcinj

1

您需要在其他地方保持对OnBitmapProcessedListener的强引用,以确保GC不清除WeakReference。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接