web-dev-qa-db-ja.com

Androidのハンドラとメモリリーク

以下のコードをご覧ください。

public class MyGridFragment extends Fragment{

     Handler myhandler = new Handler() {
    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
        case 2: {

            ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
            urls.addAll(theurls);
            theimageAdapter.notifyDataSetChanged();
            dismissBusyDialog();
            break;
        }}}};
         }

このようなハンドラーを使用すると、「ハンドラーは静的である必要があり、そうでない場合はメモリーリークが発生しやすい」という警告が表示されます。誰かがこれを行う最善の方法は何ですか?

46
Rasmus

最近、自分のコードで似たようなものを更新しました。匿名のHandlerクラスを保護された内部クラスにしたところ、Lint警告はなくなりました。以下のコードのようなものがあなたのために働くかどうかを確認してください:

public class MyGridFragment extends Fragment{

    static class MyInnerHandler extends Handler{
        WeakReference<MyGridFragment> mFrag;

        MyInnerHandler(MyGridFragment aFragment) {
            mFrag = new WeakReference<MyGridFragment>(aFragment);
        }

        @Override
        public void handleMessage(Message message) {
            MyGridFragment theFrag = mFrag.get();
            switch (message.what) {
            case 2:
                ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                theFrag.urls.addAll(theurls);
                theFrag.theimageAdapter.notifyDataSetChanged();
                theFrag.dismissBusyDialog();
                break;
            }//end switch
        }
    }
    MyInnerHandler myHandler = new MyInnerHandler(this);
}

「theFrag」を置く場所を変更する必要があるかもしれません。私はそれらが参照したものについてしか推測できなかったので。

100

ここに、あなたが使用できるようにした、ちょっとした便利な小さなクラスを示します。残念ながら、匿名の静的内部クラスを持つことはできないため、まだかなり冗長です。

import Java.lang.ref.WeakReference;
import Android.os.Handler;
import Android.os.Message;

/** A handler which keeps a weak reference to a fragment. According to
 * Android's lint, references to Handlers can be kept around for a long
 * time - longer than Fragments for example. So we should use handlers
 * that don't have strong references to the things they are handling for.
 * 
 * You can use this class to more or less forget about that requirement.
 * Unfortunately you can have anonymous static inner classes, so it is a
 * little more verbose.
 * 
 * Example use:
 * 
 *  private static class MsgHandler extends WeakReferenceHandler<MyFragment>
 *  {
 *      public MsgHandler(MyFragment fragment) { super(fragment); }
 * 
 *      @Override
 *      public void handleMessage(MyFragment fragment, Message msg)
 *      {
 *          fragment.doStuff(msg.arg1);
 *      }
 *  }
 * 
 *  // ...
 *  MsgHandler handler = new MsgHandler(this);
 */
public abstract class WeakReferenceHandler<T> extends Handler
{
    private WeakReference<T> mReference;

    public WeakReferenceHandler(T reference)
    {
        mReference = new WeakReference<T>(reference);
    }

    @Override
    public void handleMessage(Message msg)
    {
        if (mReference.get() == null)
            return;
        handleMessage(mReference.get(), msg);
    }

    protected abstract void handleMessage(T reference, Message msg);
}
11
Timmmm

ADT 20 Changes により、静的にする必要があるようです。

新しいリントチェック:

Fragmentクラスがインスタンス化可能であることを確認してください。誤ってフラグメントの内部クラスを非静的にしたり、デフォルトのコンストラクターを忘れたりした場合、構成の変更後にシステムがフラグメントを再インスタンス化しようとすると、ランタイムエラーが発生する可能性があります。

ハンドラーリークを探す:このチェックは、ハンドラーの内部クラスが外部クラスへの暗黙的な参照を保持していないことを確認します。

5
Geobits

AccountManagerまたはPendingIntentに関するドキュメントを読むと、一部のメソッドが引数の1つとしてHandlerを取ることがわかります。

  • onFinished-送信が完了したときにコールバックするオブジェクト。コールバックがない場合はnull.
  • ハンドラー-コールバックが発生するスレッドを識別するハンドラー。 nullの場合、コールバックはプロセスのスレッドプールから発生します。

状況を想像してください。一部のアクティビティはPendingIntent.send(...)を呼び出して、Handlerの非静的な内部サブクラスを配置します。そして、活動は破壊されます。しかし、内部クラスは生きています。

内部クラスは、破壊されたアクティビティへのリンクを保持しています。ガベージコレクションはできません。

そのようなメソッドにハンドラーを送信する予定がない場合は、心配する必要はありません。

4
QuickNick

私は同じ問題にぶつかりますが、このトピックの1つであり、多くの質問があり、回答が少ないことがわかりました。私の解決策は簡単であり、誰かを助けることができることを願っています:

/* BEFORE */
private Handler mHandler= new Handler() {
        @Override public void handleMessage(Message msg) {
        this.doSomething();
    };
};

Runnableを実行するだけの静的なHandlerサブクラスを作成できます。実際のハンドラーインスタンスは、インスタンス変数にアクセスできるランナブルを介して何をすべきかを知っています。

/* AFTER */
static class RunnableHandler extends Handler {
    private Runnable mRunnable;
    public RunnableHandler(Runnable runnable) { 
        mRunnable = runnable;
    }
    @Override public void handleMessage(Message msg) {
        mRunnable.run();
    };
}
private RunnableHandler mHandler = new RunnableHandler(new Runnable() {
    @Override public void run() {
        this.doSomething();
    } });

機能は同じですが、警告は消えています。

0
blurkidi

この場合の簡単な解決策は次のとおりです。

Handler handler=new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message message) {
        //do your stuff here
        return false;
    } });
0
StartCoding