39

你真的会用户Handler吗?

 5 years ago
source link: http://www.10tiao.com/html/272/201807/2666454296/1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家应该很喜欢吧 Handler写成一个内部类譬如:

private Handler mMainActivityHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
      super.handleMessage(msg);
 }
};

其实包括我也很喜欢,而且一个Activity 对应一个 Handler,每一个 Handler 负责更新本 Activity 的 UI,一对一关系,分工明确。好用到爆炸。

然而 java 内部类是默认持有一个外部类的引用,因为 jvm 在把.java 源文件编译成 .class 字节码的时候,会在默认的构造函数加入外部类的引用。所以我们在内部类中也能访问外部类的引用。

然后问题就发生了,当前 Handler 持有当前 Activity 的引用,Handler 不释放,Activity 也别想释放了。

当Android应用首次启动时,framework会在应用的UI线程创建一个Looper对象。Looper实现了一个简单的消息队列并且一个接一个的处理队列中的消息。应用的所有事件(比如Activity生命周期回调方法,按钮点击等等)都会被当做一个消息对象放入到Looper的消息队列中,然后再被逐一执行。UI线程的Looper存在于整个应用的生命周期内。

当在UI线程中创建Handler对象时,它就会和UI线程中Looper的消息队列进行关联。发送到这个消息队列中的消息会持有这个Handler的引用,这样当Looper最终处理这个消息的时候framework就会调用Handler#handleMessage(Message)方法来处理具体的逻辑。
在Java中,非静态的内部类或者匿名类会隐式的持有其外部类的引用,而静态的内部类则不会。

 1public class SampleActivity extends Activity {
2
3  private final Handler mLeakyHandler = new Handler() {
4    @Override
5    public void handleMessage(Message msg) {
6      // ...
7    }
8  }
9
10  @Override
11  protected void onCreate(Bundle savedInstanceState) {
12    super.onCreate(savedInstanceState);
13
14    // Post a message and delay its execution for 10 minutes.
15    mLeakyHandler.postDelayed(new Runnable() {
16      @Override
17      public void run() /* ... */ }
18    }, 1000 * 60 * 10);
19
20    // Go back to the previous Activity.
21    finish();
22  }
23}

当activity被finish的时候,延迟发送的消息仍然会存活在UI线程的消息队列中,直到10分钟后它被处理掉。这个消息持有activity的Handler的引用,Handler又隐式的持有它的外部类(这里就是SampleActivity)的引用。这个引用会一直存在直到这个消息被处理,所以垃圾回收机制就没法回收这个activity,内存泄露就发生了。需要注意的是:15行的匿名Runnable子类也会导致内存泄露。非静态的匿名类会隐式的持有外部类的引用,所以context会被泄露掉

解决办法:

声明为静态类以后,Handler就没有了Activity的引用,无法直接引用其变量或方法了,因此我们在这里使用弱引用WeakReference来解决这个问题:

static class MyHandler extends Handler {

    WeakReference<MainActivity> activity;    public DownloadHandler(MainActivity mainActivity) {
        activity = new WeakReference<MainActivity>(mainActivity);
    }    @Override
    public void handleMessage(Message msg) {  }
}
 
           

WeakReference可以尽早地被GC回收掉,因此可以防止内存泄漏。由于这里的activity变量实际不是MainActivity类型的,因此在handleMessage中还要多一步获取与判断:

@Override
    public void handleMessage(Message msg) {        if (activity.get() != null) {
            activity.get().someMethod();
        }
}
 
           

当然每次这样调用比较麻烦,可以将WeakReference声明初始化在MainActivity中,在Handler初始化时直接传入:

public class MainActivity extends Activity {

    WeakReference<MainActivity> activity;
    MyHandler mHandler;    @Override
    protected void onCreate(Bundle savedInstanceState) {
        activity = new WeakReference<MainActivity>(mainActivity);
        mHandler = new MyHandler(activity);
    }

    class MyHandler extends Handler {        private MainActivity activity;        public MyHandler(WeakReference<MainActivity> ref) {
            activity = ref.get();
        }        @Override
        public void handleMessage(Message msg) {            if(activity != null) {
                activity.someMethod();
            }
        }
    }
}

                        喜欢 就关注吧,欢迎投稿!




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK