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


84

Android Studio:

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

だから2つの質問:

#1startServiceコンテキストの静的変数なしで静的メソッドからをどのように呼び出すのですか?
#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か?

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


メソッドのパラメーターとしてContextを渡すことはできませんか?
Juan Cruz Soler 2016年

私はこのルーチンを、コンテキストも持たない場所で呼び出すことになります。
ジョンスミス

#1はそれをパラメータ#2として同じように渡します。
njzk2 2016年

次に、コンテキストを呼び出し元のメソッドにも渡す必要があります。問題は、静的フィールドがガベージコレクションされないため、すべてのビューでアクティビティがリークする可能性があることです
Juan Cruz Soler 2016年

1
@JohnSmith開始アクティビティから(コンストラクターパラメーターまたはメソッドパラメーターを介して)必要なポイントまでカスケードします。
AndroidMechanic-Viral Patel 2016年

回答:


56

それをパラメータとしてメソッドに渡すだけです。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);
    }
}

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


コンストラクターの例を提供できますか?
ジョンスミス

クラス名がMyClassメソッドのようにパブリックコンストラクターを追加する場合public MyClass(Context ctx) { // put this ctx somewhere to use later }(これはコンストラクターです)次に、MyClassこのコンストラクターを使用する新しいインスタンスを作成します。例MyClass mc = new MyClass(ctx);
AndroidMechanic – Viral Patel 2016年

オンデマンドで渡すのは簡単ではないと思います。古いコンテキストを心配する必要がない、またはここのように静的なコンテキストなどの明らかな利点があります。非同期で呼び出される応答コールバックにコンテキストが必要だとしましょう[設定に書き込みたい場合があります]。そのため、メンバーフィールドに入力する必要がある場合があります。そして今、あなたはそれを静的にしない方法を考えなければなりません。stackoverflow.com/a/40235834/2695276が機能しているようです。
Rajat Sharma 2017

1
ApplicationContextを静的フィールドとして使用しても大丈夫ですか?アクティビティとは異なり、アプリケーションオブジェクトは破壊されませんよね?
NeoWang

50

メンバーフィールドに格納する場合は、メソッド/コンストラクターを介してシングルトンに渡されるコンテキストで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.

8
上記の例をバッシングした男をバッシングする男へ:このスレッドのポイントは、シングルトンを作成するというGoogle独自の推奨パターンと矛盾するLint警告です。
ラファエルC

7
読む:「Androidコンテキストクラスを静的フィールドに配置しないでください。これはメモリリークです(また、インスタント実行を中断します)」コンテキストクラスが何であるか知っていますか?アクティビティはその1つであり、自分で説明したように、アクティビティを静的フィールドとして保存しないでください(そうしないとメモリリークが発生します)。ただし、Contextは(アプリケーションコンテキストである限り)静的フィールドとして保存できます。これは、Contextがすべてよりも長生きするためです。(したがって、警告は無視してください)。私たちはこの単純な事実に同意できると確信していますよね?
マーカスグルノー2016年

iOSの獣医として、Androidの最初の週に...このような説明は、このコンテキストのナンセンスを理解するのに役立ちます。したがって、その糸くずの警告(ああ、警告が嫌いな方法)はぶらぶらしますが、あなたの答えは本当の問題を解決します。
eric 2016年

@Marcus子クラスが、誰がどのコンテキストでインスタンス化しているかを認識していない場合は、静的メンバーとして保存することはお勧めできません。さらに、アプリケーションコンテキストはアプリのアプリケーションオブジェクトの一部として存在し、アプリケーションオブジェクトはメモリに永久に残ることはなく、強制終了されます。一般に信じられていることとは異なり、アプリは最初から再起動されません。Androidは、新しいApplicationオブジェクトを作成し、ユーザーが以前にいた場所でアクティビティを開始して、アプリケーションが最初から強制終了されなかったという錯覚を与えます。
ラファエルC

@RaphaelCそのようなドキュメントはありますか?Androidは各プロセスの実行ごとに1つのアプリケーションコンテキストのみを保証するため、これは完全に間違っているようです。
HaydenKai

6

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


2

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

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


原則的に間違っているとは思いませんが、静的フィールドを使用する前に、話しているアクティビティには常に最大1つのインスタンスしかないことに特に注意する必要があります。アプリがさまざまな場所(通知、ディープリンクなど)から起動できるために複数のバックスタックで終わる場合、マニフェストでsingleInstanceのようなフラグを使用しない限り、問題が発生します。したがって、アクティビティからの静的フィールドを回避する方が常に簡単です。
BladeCoder 2017

android:launchMode = "singleTask"で十分なはずなので、それに切り替えます。singleTopを使用しましたが、メインアクティビティのインスタンスを常に1つだけにしたいので、それだけでは不十分でした。これがアプリの設計方法です。
Renetik 2017

2
「singleTask」は、タスクごとに1つのインスタンスのみを保証します。アプリにディープリンクや通知からの起動などの複数のエントリポイントがある場合、複数のタスクが発生する可能性があります。
BladeCoder 2017

1

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

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

public static Context ctx;

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

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

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

public static Example example;

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

解決策自体は非常に単純です。ラッピングクラスの場合でも静的インスタンスとして直接宣言する場合でも静的インスタンスにコンテキストフィールドを配置しないでください

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

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


メモリリークを発生させずにそれを行うにはどうすればよいですか?
isJulian 0019

1
できません。あなたは絶対に周りのコンテキストを渡す必要がある場合は、イベントバスに見ることができる
ゾーイ

わかりました、これは私が抱えていた問題でした。それを見てください。別の方法があるかもしれませんが、c ++コードstackoverflow.com/questions/54683863/
isJulian 0019

0

それがアプリケーションコンテキストであることを確認した場合。それは問題ではありません。これを追加

@SuppressLint("StaticFieldLeak")

1
とにかくこれを行うことはお勧めしません。コンテキストが必要な場合は、AndroidXライブラリを使用している場合はrequireContext()メソッドを使用できます。または、コンテキストを必要とするメソッドに直接渡すこともできます。または、アプリのクラス参照を取得することもできますが、そのようなSuppressLintの提案は使用しないことをお勧めします。
OleksandrNos19年

0

WeakReferenceシングルトンクラスにコンテキストを格納するために使用すると、警告は消えます

private WeakReference<Context> context;

//Private contructor
private WidgetManager(Context context) {
    this.context = new WeakReference<>(context);
}

//Singleton
public static WidgetManager getInstance(Context context) {
    if (null == widgetManager) {
        widgetManager = new WidgetManager(context);
    }
    return widgetManager;
}

これで、次のようなコンテキストにアクセスできます

  if (context.get() instanceof MainActivity) {
            ((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.