在Android开发中,使用Handler的地方不少,大体一般写法以下:java
private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { if (msg.what == 1) { mAdapter.notifyDataSetChanged(); } } };
这段代码看似没什么问题,可是里面却有一个警告,警告信息以下:android
This Handler class should be static or leaks might occur
大体意思:建议使用静态声明否者可能出现内存泄露
Handler会内存泄露?下面来讲明:git
当使用内部类(包括匿名类)来建立Handler的时候,Handler对象会隐式地持有一个外部类对象(一般是一个Activity)的引用(否则你怎么可能经过Handler来操做Activity中的View?)。而Handler一般会伴随着一个耗时的后台线程(例如从网络拉取图片)一块儿出现,这个后台线程在任务执行完毕(例如图片下载完毕)以后,经过消息机制通知Handler,而后Handler把图片更新到界面。然而,若是用户在网络请求过程当中关闭了Activity,正常状况下,Activity再也不被使用,它就有可能在GC检查时被回收掉,但因为这时线程还没有执行完,而该线程持有Handler的引用(否则它怎么发消息给Handler?),这个Handler又持有Activity的引用,就致使该Activity没法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,若是你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达以前,会有一条MessageQueue
-> Message -> Handler -> Activity的链,致使你的Activity被持有引用而没法被回收。github
Java使用有向图机制,经过GC自动检查内存中的对象(何时检查由虚拟机决定),若是GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,若是一组对象中只包含互相的引用,而没有来自它们外部的引用(例若有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,一样会被GC回收。网络
内存泄露的危害就是会使虚拟机占用内存太高,致使OOM(内存溢出),程序出错。app
对于Android应用来讲,就是你的用户打开一个Activity,使用完以后关闭它,内存泄露;又打开,又关闭,又泄露;几回以后,程序占用内存超过系统限制,FC。ide
经过程序逻辑来进行保护oop
1,在关闭Activity的时候停掉你的后台线程。线程停掉了,就至关于切断了Handler和外部链接的线,Activity天然会在合适的时候被回收。
2,若是你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就好了。post
将Handler声明为静态类this
静态类不持有外部类的对象,因此你的Activity能够随意被回收。因为Handler再也不持有外部类对象的引用,致使程序不容许你在Handler中操做Activity中的对象了。因此你须要在Handler中增长一个对Activity的弱引用(WeakReference 文章尾部会有说明)。
PS:在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
static class MyHandler extends Handler { WeakReference<Activity> mWeakReference; public MyHandler(Activity activity) { mWeakReference = new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { final Activity activity = mWeakReference.get(); if (activity != null) { if (msg.what == 1) { mAdapter.notifyDataSetChanged(); } } } }
什么是WeakReference?
WeakReference弱引用,与强引用(即咱们常说的引用)相对,它的特色是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念能够忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity以后,就算后台线程还没结束,但因为仅有一条来自Handler的弱引用指向Activity,因此GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。
在此推荐一个写好的Handler WeakReference,可直接使用。
WeakHandler.java
import java.lang.ref.WeakReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; public class WeakHandler { private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory private final ExecHandler mExec; private Lock mLock = new ReentrantLock(); @VisibleForTesting final ChainedRef mRunnables = new ChainedRef(mLock, null); public WeakHandler() { mCallback = null; mExec = new ExecHandler(); } public WeakHandler(@Nullable Handler.Callback callback) { mCallback = callback; // Hard referencing body mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler } public WeakHandler(@NonNull Looper looper) { mCallback = null; mExec = new ExecHandler(looper); } public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { mCallback = callback; mExec = new ExecHandler(looper, new WeakReference<>(callback)); } public final boolean post(@NonNull Runnable r) { return mExec.post(wrapRunnable(r)); } public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), uptimeMillis); } public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); } public final boolean postDelayed(Runnable r, long delayMillis) { return mExec.postDelayed(wrapRunnable(r), delayMillis); } public final boolean postAtFrontOfQueue(Runnable r) { return mExec.postAtFrontOfQueue(wrapRunnable(r)); } public final void removeCallbacks(Runnable r) { final WeakRunnable runnable = mRunnables.remove(r); if (runnable != null) { mExec.removeCallbacks(runnable); } } public final void removeCallbacks(Runnable r, Object token) { final WeakRunnable runnable = mRunnables.remove(r); if (runnable != null) { mExec.removeCallbacks(runnable, token); } } public final boolean sendMessage(Message msg) { return mExec.sendMessage(msg); } public final boolean sendEmptyMessage(int what) { return mExec.sendEmptyMessage(what); } public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { return mExec.sendEmptyMessageDelayed(what, delayMillis); } public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { return mExec.sendEmptyMessageAtTime(what, uptimeMillis); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { return mExec.sendMessageDelayed(msg, delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { return mExec.sendMessageAtTime(msg, uptimeMillis); } public final boolean sendMessageAtFrontOfQueue(Message msg) { return mExec.sendMessageAtFrontOfQueue(msg); } public final void removeMessages(int what) { mExec.removeMessages(what); } public final void removeMessages(int what, Object object) { mExec.removeMessages(what, object); } public final void removeCallbacksAndMessages(Object token) { mExec.removeCallbacksAndMessages(token); } public final boolean hasMessages(int what) { return mExec.hasMessages(what); } public final boolean hasMessages(int what, Object object) { return mExec.hasMessages(what, object); } public final Looper getLooper() { return mExec.getLooper(); } private WeakRunnable wrapRunnable(@NonNull Runnable r) { //noinspection ConstantConditions if (r == null) { throw new NullPointerException("Runnable can't be null"); } final ChainedRef hardRef = new ChainedRef(mLock, r); mRunnables.insertAfter(hardRef); return hardRef.wrapper; } private static class ExecHandler extends Handler { private final WeakReference<Callback> mCallback; ExecHandler() { mCallback = null; } ExecHandler(WeakReference<Callback> callback) { mCallback = callback; } ExecHandler(Looper looper) { super(looper); mCallback = null; } ExecHandler(Looper looper, WeakReference<Callback> callback) { super(looper); mCallback = callback; } @Override public void handleMessage(@NonNull Message msg) { if (mCallback == null) { return; } final Callback callback = mCallback.get(); if (callback == null) { // Already disposed return; } callback.handleMessage(msg); } } static class WeakRunnable implements Runnable { private final WeakReference<Runnable> mDelegate; private final WeakReference<ChainedRef> mReference; WeakRunnable(WeakReference<Runnable> delegate, WeakReference<ChainedRef> reference) { mDelegate = delegate; mReference = reference; } @Override public void run() { final Runnable delegate = mDelegate.get(); final ChainedRef reference = mReference.get(); if (reference != null) { reference.remove(); } if (delegate != null) { delegate.run(); } } } static class ChainedRef { @Nullable ChainedRef next; @Nullable ChainedRef prev; @NonNull final Runnable runnable; @NonNull final WeakRunnable wrapper; @NonNull Lock lock; public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { this.runnable = r; this.lock = lock; this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this)); } public WeakRunnable remove() { lock.lock(); try { if (prev != null) { prev.next = next; } if (next != null) { next.prev = prev; } prev = null; next = null; } finally { lock.unlock(); } return wrapper; } public void insertAfter(@NonNull ChainedRef candidate) { lock.lock(); try { if (this.next != null) { this.next.prev = candidate; } candidate.next = this.next; this.next = candidate; candidate.prev = this; } finally { lock.unlock(); } } @Nullable public WeakRunnable remove(Runnable obj) { lock.lock(); try { ChainedRef curr = this.next; // Skipping head while (curr != null) { if (curr.runnable == obj) { // We do comparison exactly how Handler does inside return curr.remove(); } curr = curr.next; } } finally { lock.unlock(); } return null; } } }
by anonymous