程序员人生 网站导航

从Android Handler内部类到WeakReference的知识关联

栏目:互联网时间:2014-11-03 08:21:33

Handler:

普通用法:

Handler用于处理和从队列MessageQueue中得到Message。1般我们要重写Handler的handleMessage(Message msg){}方法来处理,以下代码:

public class MainActivity extends Activity { private TextView textView; Handler normalHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.i("test",textView.getText().toString()); break; default: break; } }; }; }



问题:

这个时候Handler会被Android SDK中Lint工具检查正告你(左侧那个黄色灯泡+叹号):This Handler class should be static or leaks might occur 

缘由:

This Handler class should be static:

(知识点1)为何静态内部类可以解决这个问题呢?或说静态内部类和非静态内部类的区分是甚么?

举例:class A{int a; static int b class B{}  static class C{} }  (A是外部类,B非静态内部类,C静态内部类,a普通字段,b静态字段)

1)B非静态内部类:

可以访问A.a和A.b,也就是外部的属性都能方位。由于B隐式的持有A类对象的援用,相当于A的属性

2)C静态内部类:

C只可以访问A.b,不可以方位A.a。为何?由于C不含有A的援用,它和A类是同1个级别,只不过写到了A类的内部。

本例缘由:

Handler匿名内部类,隐式的持有了外部类Activity的援用(这就是为何你能在handleMessage()中调用MainActivity中TextView等的属性)。--->而以后调

Message message = normalHandler.obtainMessage();
normalHandler.sendMessageAtTime(message , 100*1000);

得到的message中又含有这个Handler的援用(可以看源码)。

在100秒后message被履行,这期间message被放在MessageQueue中,MessageQueue在Looper中,Looper是线程的本地变量。

也就是说MainActivity即便生命周期走完了也不会垃圾回收,为何?由于Java的垃圾回收机制,就是看1个对象有无被援用(从线程中的主要对象开始,对象之间的援用构成网状结构,如果有类的对象不在这张网上,就证明它没被援用。这就是数据结构中图的遍历,甚么连通子图,非连通子图)。而本文中1个MainActivity被Handler持有援用,Handler被Message持有援用,Message被MessageQueue持有援用,MessageQueue被Looper持有援用,Looper为线程本地变量,线程不被摧毁,它就不会被烧毁。

所以即使用户已切换、退出到别的Activity,MainActivity占有的内存仍旧不会被释放。


解决方案:

打破援用链:

1.Message在100秒后被处理,以后回收Message,然后回收MainActivity。(所以是实际上,你只要不发很长时间的Message也不会有甚么问题)

2.使Handler不持有MainActivity的援用,用弱援用WeakReference:(简单讲,就是只有WeakReference援用的对象,垃圾回收将回收该对象,以后再另写1篇援用的文章吧)


正常代码:

MyHandler handler = new MyHandler(this); public static class MyHandler extends Handler { private WeakReference<MainActivity> reference; public MyHandler(MainActivity activity) { reference = new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.i("test",textView.getText().toString()); break; default: break; } } }



------分隔线----------------------------
------分隔线----------------------------

最新技术推荐