问题原因及解决方案
项目中有不少使用Handler的地方,有时候为了方便直接Handler handler = new Handler()这种写法,但是在Java中,非静态的匿名InnerClass会持有一个OuterClass的隐式引用,而静态内部类则不会。在Android studio上会提示你如下的警告信息:
This Handler class should be static or leaks might occur
对此,Google的Romain Guy给出了建议
所以针对Romain Guy的建议,图1的的问题解决方案如下:
private static class BlurHandler extends Handler {
private final WeakReference<BlurController> mTarget;
public BlurHandler(BlurController controller) {
mTarget = new WeakReference<BlurController>(controller);
}
@Override
public void handleMessage(Message msg) {
BlurController controller = mTarget.get();
if (controller != null) {
Bitmap bitmap = (Bitmap) msg.obj;
if (bitmap != null && !bitmap.isRecycled()) {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
bitmap.getWidth() / SCALE_RATIO,
bitmap.getHeight() / SCALE_RATIO,
false);
Bitmap blurBitmap = FastBlurUtil.doBlur(scaledBitmap, BLUR_RADIUS, true);
if (blurBitmap != null) {
controller.imgBigBg.setImageBitmap(blurBitmap);
}
}
}
}
}
ps:BlurHandler在BlurController类中,注意Romain Guy提到的OuterClass可以是任何类,可以是View,Activity,或者XXUtils,我这里就是BlurController。
给出了解决方案,会有到底是如何发生的内存泄漏?
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
当activity被finish掉后,delayed message会在主线程的消息队列中存在10分钟后才会被执行,这个Message会持有一个SampleActivity中Handler的引用,而这个Handler也会持有一个SampleActivity的隐式引用,这个引用会一直存在直到message被执行,这样会防止Activity的context被垃圾回收,但同时也会泄露application的资源。
小结
- 如果使用InnerClass,请设置为static
- 对外部类持有个WeakReference