观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

  在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<Activity>? = 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

非静态内部类

泄露代码

/**
 * ===============================================================
 * 第一种情况
 * ===============================================================
 */
public class TestActivity extends AppCompatActivity {
    private static DemoBean mDemoBean;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        //当前外部类静态持有
        mDemoBean = new DemoBean();
    }

    /**
     * 未添加static静态修饰
     */
    public class DemoBean{
        private String name = "";
        private int age = 1;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

/**
 * ===============================================================
 * 第二种情况
 * ===============================================================
 */
public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        //交给单例持有
        Singleton.INSTANCE.setDemoBean(new DemoBean());
    }

    /**
     * 未添加static静态修饰
     */
    public class DemoBean{
        private String name = "";
        private int age = 1;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

抓取内存泄露

问题原因

创建内部类的时候未添加static修饰, 并且将实例的内部类传给单例或者其他类保管. 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。

解决思路

在创建任何内部类的时候养成随手添加static,并且思考接下来的业务是否需要取消static. 如非必要不创建非静态的内部类

注册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,防止反复注册。

 

Dialog对话框泄漏

问题描述

   当你在对话框的setOnCancelListener 与 setOnDismissListener 这两个回调方法里,去调用了UI相关的操作就有可能引起内存泄漏
 
问题原因  
  setOnCancelListener 与 setOnDismissListener 方法底层依然是用Handler实现的,所以与Handler内存泄漏同理。源码如下图:

 

 

 
 

End

posted on 2022-01-22 10:59  观心静  阅读(328)  评论(0编辑  收藏  举报