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比较简洁一点...)
网友评论