美文网首页
笔记之——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