Android开发 内存泄露举例


前言

  在https://www.cnblogs.com/guanxinjing/p/10701192.html 这篇博客里了解了如何抓取与分析Android的内存泄露后,在这个博客里.将记录举例会引起内存泄露的情况.在android平台一般情况下的内存泄露都指页面(Activity或者Fragment等等). 因为他们会经常的进行创建与销毁. 所以除非特别指出,否则这篇博客的举例都是指view层泄露

单例持有

单例类

object Singleton{
    private var mActivity :Activity? = null
    
    public fun setActivity(activity :Activity){
        mActivity = activity;
    }
}

这里不限于Activity,如果将fragment或者dialog 交给单例类持有,也将有相同的内存泄露情况

activity代码

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_demo)
        Singleton.setActivity(this)
    }

抓取内存泄露

解决思路

尽量不要将Activity交给单例持有,如果非要交给单例持有,也应该是弱引用持有.代码如下:

WeakReference弱引用

object Singleton{
    private var mActivity : WeakReference? = null

    public fun setActivity(activity :Activity){
        mActivity = WeakReference(activity)
    }
}

属性动画ObjectAnimator

 泄露代码

    override fun onStart() {
        super.onStart()
        startAnim()
    }

    private fun startAnim(){
        val objectAnimatorA: ObjectAnimator = ObjectAnimator.ofFloat(textView2, "translationX", 100f, -100f, 0f)
        objectAnimatorA.setRepeatCount(-1)
        //持续时间
        objectAnimatorA.duration = 3000
        objectAnimatorA.start()
    }

解决思路

在activity onStop去执行cancel动画

线程或者Handler

在启用线程或者启用Handler在Activity里执行循环工作时导致的内存泄露. 另外Timer和TimerTask导致的内存泄露也是此类型的内存泄露这里不做重复举例

泄露代码

    public lateinit var mHandler: Handler

    override fun onStart() {
        super.onStart()
        handler()
    }

    private fun handler(){
        if (!::mHandler.isInitialized){
            mHandler = Handler(Looper.getMainLooper())
        }
        mHandler.postDelayed(Runnable {
            handler()
        }, 200)
    }

抓取内存泄露

 解决思路

 下面是Handler的解决,如果你是线程循环,建议使用AtomicBoolean 原子布尔来打断循环

    public  var mHandler: Handler? = null

    override fun onStart() {
        super.onStart()
        mHandler = Handler(Looper.getMainLooper())
        handler()
    }

    override fun onStop() {
        super.onStop()
        //退出的时候将handler消息清空
        mHandler?.removeCallbacksAndMessages(null)
        //并且一定要设置为null,否则removeCallbacksAndMessages 有概率不能清除已经正在执行的代码,导致重新循环
        mHandler = null
    }

    private fun handler(){
        if (mHandler == null){
            //此处一定要增加判空
            return
        }
        mHandler?.postDelayed(Runnable {
            handler()
        }, 200)
    }

传入接口泄露

泄露代码

单例代码

object Singleton{
    public var mListener: OnListener? = null

    public fun setActivity(listener :OnListener){
        mListener = listener
    }
}

interface OnListener{
    fun onData()
}

activity代码

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_demo)
        Singleton.setActivity(object : OnListener {
            override fun onData() {

            }
        })
    }

抓取内存泄露

解决思路

 请确保在每一个在activity匿名注册的接口在,activity需要被销毁的时候设置为null

非静态内部类

注册LiveData或者MutableLiveData的观察者导致的内存泄露问题

这个内存泄露不是指Activity或者Fragment内存泄露了, 而是注册的Observer重复多次创建,这个Observer内存泄露了

问题描述

  当你在一些主页Activity里注册LiveData的观察者时,有可能是在onStart或者onResume生命周期里注册它。这个时候就有可能导致内存泄露。

抓取内存泄露

问题原因

  其实在正常情况下onStart或者onResume生命周期里注册观察者Observer也没问题。但是如果你的主页如果是不需要退出,就不会走onDestroy生命周期(特别是在一些Android物联设备上,本身主页就是桌面,且无法退出)。这个时候就会出现在其他Activity返回后在onStart或者onResume生命周期里反复注册观察者Observer。

现在抓取内存泄露时候可以看到多个内部类(就是Observer类)不会被移除。这是因为LiveData的观察者在Activity或者Fragment里不需要手动去注销,但是也是需要在onDestroy执行时候才会注销的。

解决思路

  请将它放到onCreate生命周期里注册Observer,防止反复注册。

End