小轩窗i
给时光以生命,而不是给生命以时光。 ——小菜鸟的飞行日记

用Leak Canary工具检测到anonymous class implements android.content.DialogInterfaces$OnClickListener

 

谷歌之,找到一篇文章https://corner.squareup.com/2015/08/a-small-leak.html

大段大段的英文表示没兴趣看,也看不太懂,于是咱家就一直往下拉,到fix部分:

The Startup Fix

Support only devices that use the ART VM, ie Android 5+. No more bugs! Also, no more users.

The Won’t Fix

You could also assume that these leaks will have a limited impact and that you have better things to do, or maybe simpler leaks to fix. LeakCanary ignores all Message leaks by default. Beware though, an activity holds its entire view hierarchy in memory and can retain several megabytes.

The App fix

Make sure your DialogInterface.OnClickListener instances do not hold strong references to activity instances, for example by clearing the reference to the listener when the dialog window is detached. Here’s a wrapper class to make it easy:

直接复制下面这个类

public final class DetachableClickListener implements DialogInterface.OnClickListener {

  public static DetachableClickListener wrap(DialogInterface.OnClickListener delegate) {
    return new DetachableClickListener(delegate);
  }

  private DialogInterface.OnClickListener delegateOrNull;

  private DetachableClickListener(DialogInterface.OnClickListener delegate) {
    this.delegateOrNull = delegate;
  }

  @Override public void onClick(DialogInterface dialog, int which) {
    if (delegateOrNull != null) {
      delegateOrNull.onClick(dialog, which);
    }
  }

  public void clearOnDetach(Dialog dialog) {
    dialog.getWindow()
        .getDecorView()
        .getViewTreeObserver()
        .addOnWindowAttachListener(new OnWindowAttachListener() {
          @Override public void onWindowAttached() { }
          @Override public void onWindowDetached() {
            delegateOrNull = null;
          }
        });
  }
}

 

Then you can just wrap all OnClickListener instances:

在要用到AlertDialog的类中添加:

DetachableClickListener clickListener = DetachableClickListener.wrap(new DialogInterface.OnClickListener() {
  @Override public void onClick(DialogInterface dialog, int which) {
    //    MyActivity.this.makeCroissants();
    //处理PositiveButton中的事情
} });

AlertDialog dialog = new AlertDialog.Builder(this)
  .setTitle("")
  .setMessage("")  
  .setPositiveButton("Baguette", clickListener)
  .setNegativeButton(getString(R.string.update_later), new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
      dialog.dismiss();
    }
  })
  .create();
dialog.show();   //原文中把show()放在clear后面,会报错
clickListener.clearOnDetach(dialog); 

 

The Plumber’s Fix

Flush your worker threads on a regular basis: send an empty message when a HandlerThreadbecomes idle to make sure no message leaks for long.

static void flushStackLocalLeaks(Looper looper) {
  final Handler handler = new Handler(looper);
  handler.post(new Runnable() {
    @Override public void run() {
      Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override public boolean queueIdle() {
          handler.sendMessageDelayed(handler.obtainMessage(), 1000);
          return true;
        }
      });
    }
  });
}

 

This is useful for libraries, because you can’t control what developers are going to do with dialogs. We used it in Picasso, with a similar fix for other types of worker threads.

 

posted on 2016-04-15 18:30  小轩窗i  阅读(1434)  评论(0编辑  收藏  举报