接触到Handler时,加入我们这样写代码:
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };
可以发现出现提示This Handler class should be static or leaks might occur
在 Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类不会持有外部类的引用。
即有可能出现内存泄漏。
何为内存泄漏?
持有一个即将被回收的对象或对象组的引用,但自身未执行完毕,导致持有的引用的对象无法被回收。
首先了解一下GC回收机制:
程序运行起来之后一直在使用内存,而Java通过GC自动检查内存中的对象(检查时机有虚拟机决定),若GC发现对象处于不可达状态(即销毁或不可取,详情参考JAVA对象生命周期),则GC将该对象从内存中回收。也就是说当一个对象变得不被任何引用指向就会在GC发现时被回收。若现在有一组对象,对象A和对象B中只包含互相的引用,却没有任何外部的对象持有对象A或对象B的引用,也就是说这两个对象已经没有意义了,这两个对象(这个对象组)同样属于不可达状态,那么它就会被GC回收。
举个栗子:
在Activity中我们创建一个Handler和一个线程执行网络操作
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case UPDATE_UI_TEXTVIEW: //这里执行UI操作,也就是说这里有本Activity的引用 break; } } };
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //开个线程执行网络请求 new Thread(new Runnable() { @Override public void run() { //执行网络请求,完成后向handler发消息让其更新UI Message msg = new Message(); msg.what = UPDATE_UI_TEXTVIEW; handler.sendMessage(msg); } }).start(); }
代码很简单,可以看到我们在onCreate中开启了一个子线程,在其中执行网络请求,并在完成后发消息给handler,handler在handleMessage中进行处理后更新UI。
试想现在我们开启了子线程并执行网络请求,但由于某些原因未等网络请求完成activity便销毁了。此时activity就是一个不可达的对象,那么就可能被GC发现并回收,但是,我们的子线程中网络请求并未执行完成,而巧的是我们的子线程中又含有handler的引用(其实并不巧,大多数子线程都需要更新ui所以都会含有Handler)
handler来自何方?自然来自我们即将被回收的activity
然而activity中的handler对消息处理后会更新activity的ui,自然handler就含有activity的引用,那么内存泄漏就产生了。
内存泄漏的危害是啥?
内存泄露的危害就是会使虚拟机占用内存过高,导致OOM(内存溢出),程序出错。
对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制,FC(强制关闭)。
所以我们要尽可能的避免内存泄漏,让我们的程序尽量不发生OMM,FC。
解决方案
我们可以直接在Activity中写一个静态内部类继承自Handler,在其中我们需要得到外部的activity的引用并更新ui,则采用一个弱引用进行处理
private final WeakReference
MainActivity activity = mWeakReference.get();//在需要用到activity引用时进行获取
那么这个静态内部类就如下:
private static final class MainHandler extends Handler{ private final WeakReferencemainWeakReference; private MainHandler(MainActivity mainActivity) { mainWeakReference = new WeakReference<>(mainActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity mainActivity = mainWeakReference.get(); if(mainActivity!=null){ switch (msg.what){ case 1: break; } } } }
最后在activity中调用
private MyHandler mMyHandler = new MyHandler(this);//初始化handler
然后就像之前的handler一样在子线程中发消息就可以了!
弱引用为何可以解决内存泄漏?
之前说到了内存泄漏时因为对象B持有对象A引用,对象A即将被回收而对象B却无法结束导致对象A无法被回收。
我们对Activity使用弱引用,弱引用的对象在内存不足时将被GC回收。那么接着上面的流程——activity被销毁即将被回收,子线程中含有handler的引用,handler属于静态内部类,没有activity的引用,也就不会导致内存溢出。我们已经使用弱引用的方式在handler中需要用到activity时得到activity的引用,不用时将被GC回收,也就避免了OMM。