美文网首页
笔记之——Android中Activity与Handler的纠葛

笔记之——Android中Activity与Handler的纠葛

作者: xulee | 来源:发表于2020-07-04 18:05 被阅读0次

1. 问题


当我们在 Android 的Activity 中这样使用 Handler 时(代码使用kotlin):

private val handler = object: Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
        }
    }

IDE 会提示 Handler 为非静态类型可能导致内存泄漏(其实就是不建议如此使用)

2. 原因


首先:此种创建 handler 的方式创建了一个匿名的内部类,而在Java中,匿名内部类会隐式持有外部类的引用,也就是这个 handler 会持有 Activity的引用,从而导致Activity即使关闭了,可能仍在被Handler 引用 ,Activity无法回收,发生OOM

3. 解决方法


既然 非静态内部类 和 匿名内部类 都会 隐式持有外部类的引用,那么我就创建一个静态内部类好了,就不会隐式持有外部类的引用了

private val handler = MyHandler()
    
class MyHandler: Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
        }
    }

在kotlin 中class 里面写class默认就是静态class (Java 的话,添加 static 关键字效果一样),如果要创建非静态的,在class 前添加 inner 关键字,这样就可以使用外部类的变量和方法了,但也就不是我们想要的了

当然,上面的只是把隐式引用的问题解决了,我们还需要把 Activity 传进去,这样才可以在Handler 中调用 activity的方法,但直接传进去接收就肯定会导致activity被 handler 强引用而无法回收,最终导致OOM,所以我们可以传一个 弱软用的Activity,最终代码如下(kotlin):

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)
        handler.sendEmptyMessage(0)
    }

    private val handler = MyHandler(WeakReference(this))

    class MyHandler(private val act: WeakReference<MyActivity>): Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when(msg.what) {
                0 -> act.get()?.hello()
            }
        }
    }

    fun hello() {}

    override fun onDestroy() {
        super.onDestroy()
        handler.removeCallbacksAndMessages(null) // 别忘了在关闭页面的时候移除所有消息
    }
}

4. 总结:

Handler 作为Android中经常被用到的组件之一,可以用来当作定时器处理延时任务,或者跨页面之间通信等等
比如,在一个静态类中如下声明:

val mainHandler = Handler(Looper.getMainLooper())
    
fun runOnUiTaskDelay(delayTime: Long, method: () -> Unit) {
        mainHandler.postDelayed(method, delayTime)
}

就可以在Activity/Fragment中如此使用:

runOnUiTaskDelay(100) {
        //TODO
}

或者自己封装成一个界面通信组件,都是可以的。(我比较懒,代码用kotlin比较简洁一点...)

相关文章

网友评论

      本文标题:笔记之——Android中Activity与Handler的纠葛

      本文链接:https://www.haomeiwen.com/subject/gknuqktx.html