web-dev-qa-db-ja.com

警告:Androidコンテキストクラスを静的フィールドに配置しないでください。これはメモリリークです(また、インスタントランを中断します)

Android Studio:

Androidコンテキストクラスを静的フィールドに配置しないでください。これはメモリリークです(また、インスタントランを中断します)

2つの質問:

#1コンテキストの静的変数なしで静的メソッドからstartServiceを呼び出すにはどうすればよいですか?
#2静的メソッドからlocalBroadcastを送信する方法(同じ)。

例:

public static void log(int iLogLevel, String sRequest, String sData) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(mContext, LogService.class);
        intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        mContext.startService(intent);
    }
}

または

        Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
        intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
        intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
        intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);

mContextを使用せずにこれを行う正しい方法は何でしょうか?

注:私の主な質問は、呼び出し元のメソッドが存在するクラスにコンテキストを渡す方法です。

74
John Smith

メソッドにパラメーターとして渡すだけです。 Contextを開始するためだけにIntentの静的インスタンスを作成する意味はありません。

メソッドは次のようになります。

public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(ctx, LogService.class);
        intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        ctx.startService(intent);
    }
}

質問に対するコメントからの更新:開始アクティビティから(コンストラクターパラメーターまたはメソッドパラメーターを介して)コンテキストを必要なポイントまでカスケードします。

メンバーフィールドに保存する場合は、context.getApplicationContext()を渡すか、メソッド/コンストラクターを介してシングルトンに渡されるコンテキストでgetApplicationContext()を呼び出してください。

ばか証明の例(誰かがアクティビティを渡す場合でも、アプリのコンテキストを取得し、それを使用してシングルトンをインスタンス化します):

public static synchronized RestClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RestClient(context.getApplicationContext());
    }
    return mInstance;
}

ドキュメントによると、getApplicationContext(): "現在のプロセスの単一のグローバルアプリケーションオブジェクトのコンテキストを返します。"

これは、「getApplicationContext()」によって返されるコンテキストがプロセス全体に渡って存続することを意味します。したがって、アプリケーションの実行中に常に存在するため、静的参照をどこに格納しても問題ありません/ singletonsによってインスタンス化されます)。

大量のデータを保持しているビュー/アクティビティ内のコンテキストと比較して、アクティビティが保持しているコンテキストをリークすると、システムは明らかに良くないリソースを解放できなくなります。

コンテキストによるアクティビティへの参照は、アクティビティ自体と同じライフサイクルを維持する必要があります。そうでない場合、コンテキストの人質が保持され、メモリリークが発生します(これがlint警告の原因です)。

編集:上記のドキュメントの例をバッシングする人には、コード内に私が書いたことについてのコメントセクションさえあります:

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.
47
Marcus Gruneau

これは単なる警告です。心配しないで。アプリケーションコンテキストを使用する場合は、プロジェクト内のすべてのシングルトンクラスを保存するために使用される「シングルトン」クラスに保存できます。

4
Licat Julius

一般に、コンテキストフィールドを静的として定義することは避けてください。警告自体が理由を説明しています。これはメモリリークです。しかし、インスタントランを壊すことは、地球上で最大の問題ではないかもしれません

現在、この警告が表示されるシナリオは2つあります。インスタンス(最も明白なもの)の場合:

public static Context ctx;

そして、もう少しトリッキーなものがあります。コンテキストがクラスにラップされています:

public class Example{
    public Context ctx;
    //Constructor omitted for brievety 
}

そして、そのクラスはどこかで静的として定義されています:

public static Example example;

そして、警告が表示されます。

ソリューション自体は非常に単純です。静的フィールドにコンテキストフィールドを配置しないでください、ラップクラスの場合でも静的に直接宣言する場合でも。

また、警告に対する解決策は簡単です。フィールドを静的に配置しないでください。あなたの場合、コンテキストをインスタンスとしてメソッドに渡します。複数のContext呼び出しが行われるクラスの場合、コンストラクターを使用して、コンテキスト(またはそのことについてはActivity)をクラスに渡します。

これは警告であり、エラーではないことに注意してください。何らかの理由でneed静的なコンテキストがあれば、それを行うことができます。ただし、そうするとメモリリークが発生します。

1
Zoe

あなたの場合、静的フィールドとして持つことはあまり意味がありませんが、すべての場合に悪いとは思いません。あなたが今何をしているのなら、コンテキストを持つ静的フィールドを持ち、後でそれをヌルにすることができます。コンテキストを持ち、そのアプリケーションコンテキストがアクティビティコンテキストではないメインモデルクラスの静的インスタンスを作成しています。また、破棄時にnullになったアクティビティを含むクラスの静的インスタンスフィールドがあります。メモリリークがあることはわかりません。だから賢い人が私が間違っていると思うなら、気軽にコメントしてください...

また、インスタントランはここで正常に動作します...

1
Renetik