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

    End

  • 相关阅读:
    Chat Icon
    docker 容器无root 权限,如何获得docker容器里面的root权限
    yolo训练自己的数据
    jetson nano(1-1) 系统烧录和备份
    jetson nano(2)软件环境开发
    jetson nano(1-2)配置VNC
    坐标映射(remap重映射)
    opencv图像格式
    matlab相机标定导出xml文件
    jupyter安装和链接aconda
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/15832595.html
Copyright © 2020-2023  润新知