2013-04-24 14 views
12

mainUIスレッドで作成されたハンドラをActivityから渡して、あるネットワーク操作を実行するスレッドに渡します。結果を取得すると、ハンドラを使用して結果をアクティビティに返します。 Inner ClassHandler Memory Leak
Android DevelopersonDestroy()が呼び出された後でもアクティビティインスタンスが存在する


は、だから私はWeakReferenceを実施していた、とWeakReferenceを使用して、アクティビティインスタンスを保持:私はこれらのリンクを経由したとき

このアプローチでは、メモリリークで問題がありました。しかし、私はまだ活動が破壊された後でもActivityインスタンスが生きているのを見ています。

アクティビティ内にHandlerを作成し、アクティビティインスタンスをハンドラに弱参照として渡しました。
Handlerが10秒後に配信されたメッセージで応答するまでには、Activityは破棄されます。しかし、弱い参照はまだActivityインスタンスを持っており、Activityが破棄された後にToastが表示されています。

私の理解が間違っていますか?
ハンドラに配信されるメッセージをどのように処理するか説明できる人がいますが、UIはありません。を得応答に基づいて

import java.lang.ref.WeakReference; 

import android.os.Handler; 
import android.os.Message; 

public abstract class SingleParamHandler <T> extends Handler 
{ 
private WeakReference<T> mActivityReference; 

public SingleParamHandler(T activity) { 
    mActivityReference = new WeakReference<T>(activity); 
} 

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

protected abstract void handleMessage(T activity, Message msg); 

} 

import android.app.Activity; 
import android.os.Bundle; 
import android.os.Message; 
import android.widget.Toast; 

public class MainActivity extends Activity { 

MyHandler<MainActivity> handler; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main1); 
    handler = new MyHandler<MainActivity>(this); 
    new Thread(new MyRunnable(handler)).start(); 
} 

public void onDestroy() { 
    super.onDestroy(); 
    System.out.println("######## Activity onDestroy() ###### "); 
} 

private class MyRunnable implements Runnable { 
    private Handler mHandler; 
    public MyRunnable(Handler handler) { 
     mHandler = handler; 
    } 

    public void run() { 
     try { 
      Thread.sleep(10000); 
      mHandler.sendMessage(Message.obtain(handler, 1)); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 


private static class MyHandler<T> extends SingleParamHandler<T> { 

    public MyHandler(T activity) { 
     super(activity); 
    } 

    @Override 
    public void handleMessage(T act, Message msg) { 
     if(msg.what == 1) { 
      Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();; 
     } 
    } 
} 

} 

、私はここで答えを更新しています。あなたは好きなやり方でそれをするかもしれません。しかし、これは一つの方法です。

は、そうする必要はない場合Androidは本当にメモリからオブジェクトを削除するという保証はありません)SingleParamHandler

public void clear() { 
    mActivityReference.clear(); 
} 

と活動onDestroy(中に

public void onDestroy() { 
    super.onDestroy(); 
    System.out.println("######## Activity onDestroy() ###### "); 
    handler.clear(); 
} 

答えて

5

ここにWeakReferenceは必要ありません。 Handlerには、Activityへの参照が含まれています。アクティビティのonDestroy()では、Activityへの参照をnullに設定するメソッドをMyHandlerで呼び出します。 nullhandleMessage()にチェックしてください。

アクティビティのonDestroy()では、スリープ中のスレッドを中断してメッセージを送信する前にシャットダウンするメソッドを呼び出すこともできます。

+0

論理に感謝します。私が知っていることは、weakreferenceが必要ではないと言っていることです。私が投稿したリンクは静的であり、メッセージを処理する前にアクティビティやサービスに弱点を付けて確認する必要があると言うリンクです。それは矛盾していませんか?これに関するあなたの知識は評価されるでしょう.. !! – Mani

+0

@Maniこの提案は、私と同じ方向に進みます(登録/登録解除アクティビティ)。私は仮定を少なくしていました。デビッド、私はまだこの単純なシナリオでは、 'onDestroy()'が実行することが保証されていないので、Handlerが 'Activity.isDestroyed()'をチェックする方が簡単だろうと思いますか? –

+1

リンク先の記事は特定の状況をカバーしており、そこに書かれているすべてのものに同意しません。カバーされるケースは、「ハンドラ」に送信される「遅延メッセージ」です。この記事では、メッセージが10分間で配信される「ハンドラ」に送信されるケースについて説明します。このメッセージは 'Handler'への参照を持ち、' ​​Handler'は 'Activity'への参照を持ち、' ​​Handler'も 'Activity'もガベージコレクタ**で削除できないことを意味します。** whileこのメッセージはまだキューに残っています**。 –

3

を以下の機能を追加しました。言い換えれば、アクティビティオブジェクトは、onDestroy()が呼び出された後でも(十分なメモリがある場合)、メモリに保持されます。一方、十分なメモリがない場合は、onDestroy()と呼ばれるという保証はありません。まったく逆に、あなたの現在の活動(Androidのバージョンに応じて)にonPause()を呼び出した後、Androidはあなたの全プロセスを殺すことができます。

私はあなたの目的のために従うべきよりよい道があると思います。あなたがやりたいことは、に、にデタッチ、おそらくに変更してください。(例:コンフィギュレーション変更の場合)あなたのサービスへのアクティビティ。ガベージコレクターがあなたのために仕事をすることを望んではいけません。むしろ、明示的にしてください。

サブクラスActivityと、startActivity()startActivityForResult()のライフサイクルメソッドをオーバーライドして、サービス担当者に現在担当してもらうようにしてください。もちろん、いくつかのコールバックは保証されていないので、これはベストエフォートアプローチですが、それは危険ではない特定の状況でのみ重要です。たとえば、あなたの活動はonPause()であなたのサービスから切り離されることはありませんが、直ちに殺される可能性があります。しかしどちらのサービスも同じプロセスで実行されるため、同時に殺されます。または、別のプロセスで実行されますが、Androidは接続が切断されていることに気づくでしょうし、サービスを停止することもあれば停止させることもありません。そうでない場合は、接続損失に対処できるように堅牢な方法で実装するだけです。あなたのコメントを読んだ後

更新

:そうだね、私は、特にそれに対応していませんでした。私は上記のコードを考える

を破壊された活動で作成されたハンドラに送信されるメッセージを回避する方法を考え出すと、あなたが本当にただToast秒を表示することを想定しています

それが存在する限り、アクティビティとともに、以下のアプローチが役立つはずです。

  • Threadが複数のアクティビティを処理することになっている場合は、アクティビティを作成した後にアクティビティが登録できるように、そのアクティビティを拡張します。 ThreadのサービスがActivityのサービスの場合は、ThreadRunnable ')の構成にHandlerの参照と一緒にActivityの参照を渡してください。
  • ThreadHandlerでメッセージを送信する前に、activity.isDestroyed()にチェックを入れてください。アクティビティが破棄されていない場合は、メッセージを送信します。アクティビティが破棄された場合は、メッセージを送信しないでください。
  • は、あなたのスレッドは、サーバーに複数の活動、終了する必要がありますいずれかのかどうかに応じて、それはRunnablerun()方法やそれがActivityが破壊されていることを発見した場合、それはnullActivity参照です設定します。

これで上記のコードが修正されるはずです。しかし、シナリオが大きくなる場合は、他のアプローチが適しているかもしれません。

+1

resopnseありがとうございます。私は、becozのonDestroy()が呼び出されたことをActivityインスタンスが削除されないことを理解しています。しかし、私は破壊されているアクティビティで作成されたハンドラにメッセージが送られるのを避ける方法を考え出しています... !!私は、ハンドラがアクティビティに縛られていないことを知っている、それはメインのUIスレッドですが、これをどう対処するのだろうか? – Mani

+0

@Mani私の答えを更新しました。 –

関連する問題