精华内容
下载资源
问答
  • 很多时候我们为了避免 ANR 而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等api都借助Handler处理,但 Handler 不是万能的,对于 Handler 的使用代码编写一不规范即有可能造成内存泄漏。...

    原因:

    Handler 的使用造成的内存泄漏问题应该说是最为常见了,很多时候我们为了避免 ANR 而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等api都借助Handler来处理,但 Handler 不是万能的,对于 Handler 的使用代码编写一不规范即有可能造成内存泄漏。另外,我们知道 Handler、Message 和 MessageQueue 都是相互关联在一起的,万一 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有。

    由于 Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity 的生命周期保持一致,故很容易导致无法正确释放。

    分析:

    对于handler泄漏其实是由于消息队列持有对handler的引用,而handler又持有activity的隐式引用,这个引用会保持到消息得到处理,而导致activity无法被垃圾回收器进行回收,而导致内存泄漏  ,使用static+WeakReference可以解决内存泄漏问题,不过不写也无所谓 , 不加static的默认会在handler构造函数加上activity这个参数,只要handler没有被回收,就会泄露。加static目的是为了防止泄露,为什么会泄露,就是因为没回收,没回收是因为还引用着。只有postDelayed的时候才会有泄露问题,因为delayed的时候activity的引用还保持着,所以只要delayed完了就能回收了,大多数情况下根本不必用加static。

    说了这么多搬砖好,下面呈上如何搬好砖:

    
    
    import android.app.Activity;
    import android.os.Handler;
    import android.os.Message;
    
    import java.lang.ref.WeakReference;
    
    /**
     * Description Handler内存泄漏的处理类
     * Created by 
     * Date 
     */
    public class MyHandler extends Handler {
        private WeakReference<Activity> mActivity;
        private HandleMessageCallBack handleMessageCallBack;
    
        public MyHandler(Activity activity, HandleMessageCallBack handleMessageCallBack) {
            mActivity = new WeakReference<>(activity);
            this.handleMessageCallBack = handleMessageCallBack;
        }
    
        @Override
        public void handleMessage(Message msg) {
            Activity activity = mActivity.get();
            if (activity != null) {
                //做操作
                handleMessageCallBack.messageCallBack(msg);
            }
        }
    
        //接口回调Message
        public interface HandleMessageCallBack {
            void messageCallBack(Message msg);
        }
    }
    

     

    展开全文
  • 这几天使用kotlin写一个项目,用到handler,然后提醒提警告 This Handler class should be static or leaks might occur...... I wrote that debugging code because of a couple of memory leaks I found in the...

    这几天使用kotlin写一个项目,用到handler,然后提醒提警告

    This Handler class should be static or leaks might occur......
    
    I wrote that debugging code because of a couple of memory leaks I 
    found in the Android codebase. Like you said, a Message has a 
    reference to the Handler which, when it's inner and non-static, has a 
    reference to the outer this (an Activity for instance.) If the Message 
    lives in the queue for a long time, which happens fairly easily when 
    posting a delayed message for instance, you keep a reference to the 
    Activity and "leak" all the views and resources. It gets even worse 
    when you obtain a Message and don't post it right away but keep it 
    somewhere (for instance in a static structure) for later use. 

    百度后才发现是handler内存泄露的问题(原谅小木子是菜鸟)
    参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1106/1922.html
    http://blog.csdn.net/donkor_/article/details/78796518?utm_source=tuicool&utm_medium=referral
    这里写图片描述

    原代码

        private var mHandler1: Handler = object : Handler() {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                if (msg != null) {
                    when (msg.what) {
                        CKey.WHAT_TWO -> {
                            verify_tv_again.text = "获取验证码"
                            verify_tv_time.text = ""
                            verify_next.isEnabled = true
                            verify_tv_again.isEnabled = true
                            goToAccountInfoActivity()
                        }
                    }
                }
            }
        }

    然后按照网站的提示,做了修改:

    private class MyHandler(activity: VerifyActivityActivity) : Handler() {
            private val mActivity: WeakReference<VerifyActivityActivity> = WeakReference(activity)
    
            override fun handleMessage(msg: Message) {
                if (mActivity.get() == null) {
                    return
                }
                val activity = mActivity.get()
                when (msg.what) {
                    102 -> {
                        activity!!.verify_tv_again.text = "李振华"
                        activity.verify_tv_time.text = ""
                        activity.verify_next.isEnabled = true
                        activity.verify_tv_again.isEnabled = true
    //                    activity.goToAccountInfoActivity()
                    }
                    else -> {
                    }
                }
            }
        }

    这里写图片描述

    初始化参数的时候声明一个即可使用(黄色背景即有警告 ,修改后没有了黄色警告)

        var handler12 = MyHandler(this)
        handler12.sendEmptyMessage(102)

    效果

    这里写图片描述

    大致讲解:
    首先是拿到activity的弱引用 private val mActivity: WeakReference = WeakReference(activity)
    然后在需要修改的view之间使用这个引用查找响应的控件做UI修改即可
    activity!!.verify_tv_again.text = “李振华”
    activity.verify_tv_time.text = “”
    activity.verify_next.isEnabled = true
    activity.verify_tv_again.isEnabled = true

    !!是kotlin非空的意思然后就解除警告,详细讲解参考里面有,就不复制粘贴了,只贴了代码和效果图

    展开全文
  • Handler内存泄露处理 关于Handler内存泄露可以看下面的博客,我经常看的博主 Android 内存泄露:详解 Handler 内存泄露的原因 Handler的内存泄露就是Handler默认持有了Activity或这Fragment的引用,从而导致...

    1. 关于Handler内存泄露可以看下面的博客,我经常看的博主
      Android 内存泄露:详解 Handler 内存泄露的原因

    Handler的内存泄露就是Handler默认持有了Activity或这Fragment的引用,从而导致Activity销毁后,Handler还持有Activity的引用导致Activity不能被gc回收。解决方案就是静态内部类+弱引用,使用方式可以学Fragment在Activity的绑定与解绑方式,因为静态类不会持有外部类的 引用。

    我用的kotlin,Java的方式代码也是一样,自己去写了,懒得贴了
    /**
         * Handler,避免内存泄露
         */
        private class CustomHandler : Handler() {
            private lateinit var weakReference: WeakReference<HomeFragment>
    
            fun attachHandler(homeFragment: HomeFragment) {
                weakReference = WeakReference(homeFragment)
            }
    
            fun detachHandler() {
                weakReference.clear()
            }
    
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                when (msg?.what) {
                    100 -> {
                        // 然后这里使用weakReference.get()获取弱引用保存的对象
                        // 我这里是HomeFragment
                    }
                }
    
            }
        }
    

    Handler的三种用法

    模拟子线程发送数据

    new Thread(new Runnable() {
                @Override
                public void run() {
                    // 休眠5秒
                    SystemClock.sleep(5000);
                    Message message = Message.obtain();
                    message.obj = "Handler赋值";
                    //handler1.sendMessage(message);
                    // 如果在休眠5秒的时间内退出界面(界面销毁),
                    // 但是当休眠5秒结束此处仍然会发送消息
                    // 就会造成内存泄漏
                    if (handler2 != null) handler2.sendMessage(message);
    
                    handler3.post(new Runnable() {
                        @Override
                        public void run() {
                            tvHandler.setText("Handler赋值");
                        }
                    });
                }
            }).start();
    

    1、第一种用法

    /**
         * 第一种用法
         */
        private Handler handler1 = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                tvHandler.setText(msg.obj.toString());
            }
        };
    

    2、第二种用法,推荐使用第二种用法

     /**
         * 第二种用法,推荐使用第二种用法
         */
        private Handler handler2 = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                tvHandler.setText(msg.obj.toString());
                return false;
            }
        });
    

    为什么推荐第二种用法,我们可以查看Handler源码的消息分发

    /**
         * Handle system messages here.
         */
        public void dispatchMessage(@NonNull Message msg) {
            // 1、此处是Handler的post用法,第三种用法
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                // 2、此处就是我们第二种用法传入的callback
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                // 3、此处就是第一种用法,算是Handler消息分发最后的倔强,哈哈
                // 用来保底接收数据
                handleMessage(msg);
            }
        }
    

    3、Handler第三种用法,Post方法,注释1处执行的就是post传递的Runnable对象

    private Handler handler3 = new Handler();
    
    // 直接在子线程中使用post方法刷新UI
    handler3.post(new Runnable() {
                        @Override
                        public void run() {
                            tvHandler.setText("Handler赋值");
                        }
                    });
    

    查看post方法的源码,我们看到Handler源码中将Runnable对象
    封装到了Message对象中。

    public final boolean post(@NonNull Runnable r) {
           // post方法还是调用的sendMessageDelayed,最终
           // 还是会调用到sendMessageAtTime
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
    // 通过如下代码,我们看到将Runnable对象封装到了Message中
    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            // 将Runnable对象封装到了Message的callback中
            m.callback = r;
            return m;
        }
    

    最后我们回到dispatchMessage方法

    public void dispatchMessage(@NonNull Message msg) {
            // 从上述源码分析,我们知道此处就是post方法的使用
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    最后看到handleCallback方法,其实就是调用了Runnable的run方法
    执行run方法中的代码

    private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    Handler的内存泄漏处理

    1、弱引用处理(代码在最上方)

    2、删除消息和回调callback

    这种方式需要有消息在消息队列并且使用第二种方式使用Handler,
    否则这种处理内存泄漏的方式应该还是会造成内存泄漏

    @Override
        public void onDestroy() {
            super.onDestroy();
            // 清除消息和回调的callback
            handler2.removeCallbacksAndMessages(null);
        }
    

    我验证过如下方式,在5秒消息等待之前销毁Activity,使用第一种方式创建爱女Handler还是会发送消息,还是会造成内存泄漏

    new Thread(new Runnable() {
                @Override
                public void run() {
                    SystemClock.sleep(5000);
                    Message message = Message.obtain();
                    message.obj = "Handler赋值";
                    message.what = 100;
                    if (handler2 != null) handler1.sendMessage(message);
                }
            }).start();
    

    即使调用了removeCallbacksAndMessages方法,第一种方式创建的
    Handler仍然能收到消息,造成内存泄漏

    private Handler handler1 = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                LogUtils.d(TAG, "我还是收到消息了");
                tvHandler.setText(msg.obj.toString());
            }
        };
    

    3、Handler置空

    将Handler置空之后,也就不会再继续发送消息了

    @Override
        public void onDestroy() {
            super.onDestroy();
            // 为了保险可以清除一次数据
            handler2.removeMessages(100);
            // 将Handler置空,这个跟第一种弱引用有异曲同工
            handler2 = null;
        }
    

    我们解释一下为啥清除数据是可选的,比如我们模拟消息发送的例子,
    1、休眠5秒之后在发送消息,当在休眠5秒的时间段内退出界面,此时消息
    还没有存储到消息队列
    ,所以此时清除消息是没有消息可清除的。

    就像如下代码

    SystemClock.sleep(5000);
    Message message = Message.obtain();
    message.obj = "Handler赋值";
    message.what = 100;
    if (handler2 != null) handler2.sendMessage(message);
    

    2、比如如下代码,清除数据是会生效的,因为此时已经将消息发送到消息队列中。

    Message message = Message.obtain();
    message.obj = "Handler赋值";
    message.what = 100;
    // 使用Handler提供的延迟消息发送,此时消息已经存到消息队列
    if (handler2 != null) handler2.sendMessageDelayed(message, 5000);
    

    因此,推荐使用第一种和第三种处理内存泄漏的方式

    展开全文
  • Handler内存泄漏

    2019-05-24 22:05:46
    Handler内存泄漏 关于Handler内存泄漏及解决办法: 原因: handler会持有activity的匿名引用 (因为内部类的原因,具体参考内部类实现),当activity要被回收时,因为handler在做耗时操作而没有被释放,从而内存...

    Handler内存泄漏

    1. 关于Handler内存泄漏及解决办法:
      • 原因:

        1. handler会持有activity的匿名引用 (因为内部类的原因,具体参考内部类实现),当activity要被回收时,因为handler在做耗时操作而没有被释放,从而内存泄漏
        2. 实例化Handler对象后,它就会主动与主线程的Looper的消息队列关联起来,所有发送到消息队列的Message都会拥有一个对Handler的引用,所以才由Looper处理消息时的回调Handler.handlerMessage(Message)来处理消息
      • public class SampleActivity extends Activity {
            private Handler mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    //...
                }
            }
        }
        
      • 解决办法:

        • 将Handler设置为静态内部类(具体参考静态内部类和内部类区别),并使使Handler持有对Activity的弱引用

           public class SampleActivity extends Activity {
                   .......
                private static class MyHandler extends Handler {
                       private final WeakReference<SampleActivity> mActivity;
                       public MyHandler(SampleActivity activity) {
                           myActivity = new WeakReference<SampleActivity>(activity);
                       }
          	 		@Override
           	 		public void handleMessage(Message msg) {
           	 			SampleActivity activity = mActivity.get();
               			if (activity != null) {
           			...
          	 		}
                }
             }
          }
          
        • 在activity生命周期onDestroy()中调用Handler.removeCallback()方法

          
          // 清空消息队列,移除对外部类的引用
          @Override
          protected void onDestroy() {
              super.onDestroy();
              mHandler.removeCallbacksAndMessages(null);
          
          }
          //Handler源码中removeCallbacksAndMessages()注释含义
          /**
           * Remove any pending posts of callbacks and sent messages whose
           * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
           * all callbacks and messages will be removed.
           */
          public final void removeCallbacksAndMessages(Object token) {
              mQueue.removeCallbacksAndMessages(this, token);
          }
          

    内部类实现

    1. 内部类和静态内部类以及匿名内部类的应用
      • 内部类的优势:

        • 内部类方法可以访问盖内定义所在作用域的数据,包括私有数据

        • 内部类可以对同一个包中其他类隐藏

        • 想定义一个回调函数而且不像编写大量代码时,使用匿名内部类比较简洁

      • 原理:

        • 内部类的对象总有一个隐式引用,它指向了创建它的外部类对象

        • 编译器修改了所有内部类的构造器,添加了一个外围类的引用参数:

          public class Outer {
              ...
          class Inner {
              ...
              //Outer为外部类,Inner为内部类
              Outer out;
              public Inner(Outer outer) {
              out = outer;
          	}
          }
          }
          
      • 静态内部类:
        使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围类的对象。
        因此可以将内部类声明为static,以便取消产生的引用

    参考:
    https://juejin.im/post/5ccaa95ae51d453b222b7953

    https://github.com/francistao/LearningNotes/blob/master/Part1/Android/Handler内存泄漏分析及解决.md

    展开全文
  • 所以这里来再次总结下为了防止Handler内存泄漏有哪些手段? 文章目录1. Handler为什么会造成内存泄漏?2. 预防Handler内存泄漏?2.1 使用静态Handler2.2 清空消息队列2.3 使用静态Handler+弱引用WeakReference3. ...
  • Handler 内存泄漏

    2019-07-13 21:39:22
    内存泄漏 当系统运行到某一时刻,某些占用着一定内存空间的操作或对象在之后都不会使用了,所以此时系统理应将这些内存空间给释放掉给接下来的系统运行使用,但是因为一些特殊的原因,从而导致这部分的内存空间一直...
  • 一、Handler内存泄漏原因 handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类...
  • handler内存泄露

    2016-09-03 20:21:13
    Handler 内部类持有 外部类Activity的引用,如果Activity退出而Handler还有延迟处理的消息没有处理完,会导致Activity不能回收,反复如此会导致内存泄露。 解决方案一: onDestroy时清除消息,mHandler....
  • Android 解决Handler内存泄漏问题前言更多描述解决方法自定义弱引用Handler创建BaseActivity继承使用完事 前言 最近在维护老项目的时候发现,使用handler延时处理的事件再activity结束后仍旧再执行,导致activity...
  • 14.Android 控制Handler内存泄露 技巧

    千次阅读 2015-09-21 13:47:35
    Android 控制Handler内存泄露 技巧Android 控制Handler内存泄露 技巧 Handler 警告 Handler 内存泄露缘由 解决思路 Handler 通用模板 Handler 警告 In Android, Handler classes should be static or leaks might ...
  • 安卓handler内存泄漏分析及解决
  • Android Handler内存泄漏

    2017-01-18 14:58:19
    Handler是Android常用的工具,但是使用不当容易引起内存泄漏。 泄漏的原因是:当Handler声明为非静态(匿名)内部类,外部引用Activity时,会组织GC回收Activity。
  • Handler内存泄漏分析及解决
  • 说到内存泄漏,可能首先就想到创建非静态Handler内部类,导致Handler持有外部Activity引用 class MyHandler extends Handler{ @Override public void handleMessage(Message msg) { } } 网上有很多说处理方法...
  • Handler内存泄露原理及解决方法
  • Android Handler 内存泄露

    2016-01-23 20:09:55
    内存泄露:java中的垃圾(GC),垃圾回收的机制受java虚拟机的控制。java虚拟机会不定时的执行 System.gc(); 一个本该被回收的对象A,被一个生命周期还没有完成的对象B所持有,导致该被回收的对象A无法被回收。 ...
  • android handler内存泄漏问题
  • Handler,Looper,Message原理关系,及解决Handler内存泄漏 本文主要讲述Handler机制的原理,和基本用法,通过Handler线程间通信 原理 Android消息机制:每个线程都有一个消息队列MessageQueue,用来存放和处理消息...
  • handler内存泄露问题

    2016-11-23 21:18:24
    因为android自身原因,在程序运行中,不能做耗时操作,耗时操作必须在子线程中运行,所以google 提供了handler,但是handler如果处理不好,很容易造成内存泄露,最终导致oom。废话不多说,处理方式时分两种 第一种...
  • Handler其实大多的内存泄漏,都是Activity想关闭了,可是有个对象仍持有他的强引用,这个倒是小事,因为可达性算法能处理好这种情况。但是,考虑一个延时消息的情况。ActivityThread的main函数入口,创建了主线程的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,854
精华内容 12,741
关键字:

handler内存泄漏处理