Handler笔记

来源:互联网 发布:温州加工中心编程培训 编辑:程序博客网 时间:2024/05/04 14:40

关于使用Handler会泄漏内存的问题

Handler我一直都在使用,已经是常用的一个类,但是最近的我发现我关闭了Activity但是Handler还在运行。于是我想到了问题所在,Handler泄漏了。

博客地址:http://blog.csdn.net/sinat_17314503/article/details/53876936

为什么Handler会泄露

这个是十分平常的写法,好像没有什么问题喔!!!

 private Handler mhandler = new Handler(){    @Override    public void handleMessage(Message msg) {      super.handleMessage(msg);      Log.e("showme", "没有处理过的Handler" + msg.arg1);    }  };

大家都这么写,好像是标准吧,但是作为一个程序员有必要把代码写得更优雅。没错对于一些简单操作这种写法是没有问题的。

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less… (Ctrl+F1)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

引用一下AS提示的信息
Handler作为内部类,它可能会阻止外部类的引用被GC(对象回收),如果Handler正在为一个子线程进行消息轮循或者消息队列而不是主线程,那么
这个不是问题。但是如果Handler在主线程用使用消息轮循或者消息队列,那么你需要对Handler做一些处理,如下 声明Handler为内部静态类;在外部类使用WeakReference把自身传递进Handler静态内部类里面;在Handler里面把所有的外部的成员用WeakReference包裹传递进去。
ps:英语不是太好 翻译也不是太好

然后让你们见识一下当Activity调用了onDestory了Handler还在持有Activity对象。

建一个Runnable

private Runnable mRunnable = new Runnable() {    @Override    public void run() {      i++;      Message message = mHandler.obtainMessage();      message.what = 1;      message.arg1 = i;      mHandler.sendMessage(message);      mHandler.postDelayed(this,1000);    }  };

然后当TextView加载完后启动

showtext.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  @Override  public void onGlobalLayout() {    mhandler.postDelayed(MRunnable,1000);  }

在onDestory里面打印一下信息

 @Override  protected void onDestroy() {    super.onDestroy();    Log.e("showme", "onDestroy");  }

然后就是结果了

12-25 23:12:05.303 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler412-25 23:12:06.250 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler512-25 23:12:06.304 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler612-25 23:12:07.251 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler712-25 23:12:07.304 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler812-25 23:12:08.152 19241-19241/com.example.bt.testleak E/showme: on Stop12-25 23:12:08.158 19241-19241/com.example.bt.testleak E/showme: onDestroy12-25 23:12:08.277 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler912-25 23:12:08.337 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler1012-25 23:12:09.317 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler1112-25 23:12:09.377 19241-19241/com.example.bt.testleak E/showme: 没有处理过的Handler12

看到没有activity已经调用了onDestory了但是Handler还是继续打印,可以看出了activity还是被持续被引用没有释放。

以下是引用(参考文章在文章尾部)

•当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
•当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message)

•在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。

•如果外部类是Activity,则会引起Activity泄露 。

当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。

来分发处理该消息。

Handler泄漏了有什么危害

只有一个,那就是虚拟机占用内存过高,导致OOM(内存溢出),程序出错。对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制

如何防止这些内存泄漏

    static class MHandler extends Handler {        WeakReference<MainActivity> outerClass;        MHandler(MainActivity activity) {            outerClass = new WeakReference<MainActivity>(activity);        }        @Override        public void handleMessage(android.os.Message msg) {            MainActivity theClass = outerClass.get();            //使用theClass访问外部类成员和方法            theClass.showText();            Log.e("showme", "处理过的Handler" + msg.arg1);        }    }

调用

    showtext.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {        mHandler = new MHandler(MainActivity.this);        mHandler.postDelayed(mRunnable,1000);            }        });

在onDestory里面调用

    @Override    protected void onDestroy() {        super.onDestroy();        Log.e("showme", "onDestroy");        mHandler.removeMessages(1);        mHandler.removeCallbacks(mRunnable);    }

结果

12-25 23:57:29.444 31011-31011/com.example.bt.testleak E/showme: 处理过的Handler912-25 23:57:29.484 31011-31011/com.example.bt.testleak E/showme: 处理过的Handler1012-25 23:57:30.449 31011-31011/com.example.bt.testleak E/showme: 处理过的Handler1112-25 23:57:30.485 31011-31011/com.example.bt.testleak E/showme: 处理过的Handler1212-25 23:57:31.116 31011-31011/com.example.bt.testleak E/showme: on Stop12-25 23:57:31.123 31011-31011/com.example.bt.testleak E/showme: onDestroy

補充

還有一種方法可以阻止內存洩漏就是停止你那條持有Activity對象的線程停掉了,就相當于切斷了Handler和外部連接的線,Activity自然就會在合適的時候被回收。

設置一個標識

    private  Boolean isBreak = false;

在onDestory裡面把標識設置為true

    @Override    protected void onDestroy() {        super.onDestroy();        Log.e("showme", "onDestroy");        isBreak = true;    }

最後就是當其運行到這裡的時候,是直接返回,不執行下面的步驟

   private Runnable mRunnable = new Runnable() {        @Override        public void run() {            if(isBreak){                return;            }            i++;            Message message = mHandler.obtainMessage();            message.what = 1;            message.arg1 = i;            mHandler.sendMessage(message);            mHandler.postDelayed(this,1000);            Log.e("showme","Thread ID = "+Thread.currentThread().getId()+" ");        }    };

結果

12-26 15:16:18.611 12683-12683/? E/showme: handler2812-26 15:16:19.524 12683-12683/? E/showme: handler2912-26 15:16:19.611 12683-12683/? E/showme: handler3012-26 15:16:20.526 12683-12683/? E/showme: handler3112-26 15:16:20.611 12683-12683/? E/showme: handler3212-26 15:16:21.527 12683-12683/? E/showme: on Stop12-26 15:16:21.532 12683-12683/? E/showme: onDestroy

延伸:什么是WeakReference?

WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了

参考 http://blog.csdn.net/zhuanglonghai/article/details/38233069

http://www.cnblogs.com/xujian2014/p/5025650.html#_label2

0 0