アクティビティコンテキストまたはアプリケーションコンテキストを呼び出すタイミング


265

これらの2つのコンテキストが何であるかについては多くの投稿がありました。

これまでのところ理解できますが、それぞれがクラスのインスタンスです。つまり、一部のプログラマーはthis.getApplicationContext()、メモリを「漏らさない」ために、できるだけ頻繁に使用することを推奨しています。これは、他のthisActivityインスタンスコンテキストを取得する)がActivity、ユーザーが電話を傾けるかアプリを離れるたびに破棄されるを指しているためです。明らかに、ガベージコレクター(GC)がキャッチせず、メモリを使いすぎているようです。 ..

しかし、誰かがそれを使用するのが適切thisであり(現在のActivityインスタンスのコンテキストを取得する)、アプリケーションのコンテキストが役に立たない/間違っているいくつかの本当に良いコーディング例を思い付くことができますか?

回答:


408

getApplicationContext()ほとんど常に間違っています。さんHackbornは、(特に)あなたがいることを非常に明確なてきただけに使用getApplicationContext()あなたが知っているとき、なぜあなたが使用しているgetApplicationContext()とあなたがする場合にのみ必要に使用しますgetApplicationContext()

率直に言って、「一部のプログラマー」はJavaの経験が限られているためにgetApplicationContext()(またはgetBaseContext()、それほどではありませんが)使用します。彼らは、(例えば、内部クラスを実装するOnClickListenerためButtonにあるActivity)と必要ContextMyActivity.this外部クラスで取得するために使用するのではなく、オブジェクトthisを使用するgetApplicationContext()getBaseContext()Contextオブジェクトを取得します。

あなたがあなたが自由に持っている他のどの可能性のあるものよりも長持ちするかもしれない何かのためにあなたが必要であると知っているときだけあなた使います。シナリオは次のとおりです。getApplicationContext()ContextContext

  • それ自体にグローバルスコープを持つgetApplicationContext()ものに関連付けられたものが必要な場合に使用しますContext。私が使用getApplicationContext()して、例えば、WakefulIntentService静的なため、WakeLockサービスのために使用されます。それWakeLockは静的であり、作成ContextするためにPowerManagerを取得する必要があるため、を使用するのが最も安全getApplicationContext()です。

  • を介してインスタンス間で(つまり、バインディングへのハンドル)を渡したい場合getApplicationContext()Serviceからにバインドするときに使用します。Androidはこれらを介して内部的にバインディングを追跡し、バインディングを作成するへの参照を保持しています。あなたからバインドする場合は、その後、新しいインスタンスへの参照があります古いへの暗黙的な参照を持っている、そして古い缶がガベージコレクションではありません。ActivityServiceConnectionActivityonRetainNonConfigurationInstance()ServiceConnectionsContextsActivityActivityServiceConnectionActivityActivity

一部の開発者Applicationは、独自のグローバルデータにのカスタムサブクラスを使用し、それをを介して取得しgetApplicationContext()ます。それは確かに可能です。カスタムオブジェクトを1つしか持てないという理由以外に理由がない場合は、静的データメンバーを使用しApplicationます。私はカスタムApplicationオブジェクトを使用して1つのアプリを作成し、それが苦痛であることを発見しました。ハックボーン氏もこの立場に同意する

どこに行っても使用しない理由getApplicationContext()は次のとおりです。

  • これは完全Contextではなく、すべてのことをサポートActivityします。これContextを使用して実行しようとするさまざまなことは失敗しますが、ほとんどはGUIに関連しています。

  • クリーンアップされていない呼び出しで作成されたものをContextfrom getApplicationContext()が保持している場合、メモリリークが発生する可能性があります。を使用するとActivity、何かを保持している場合、Activityガベージコレクションが実行されると、他のすべてもフラッシュされます。Applicationオブジェクトは、プロセスの有効期間のために残っています。


1
この回答をありがとうございました。この回答を読む前に見つけた別のリンクも、一部の人に役立つかもしれません。stackoverflow.com/questions/7298731/…-このリンクは、メモリリークに関する私の懸念を説明しています。
Norfeldt、2011

27
@Norfeldt:参考までに、コメントのリンクはこの回答にリンクしています。
CommonsWare、2011

2
ありがとう..これはリンクでした:stackoverflow.com/questions/5796611/…これは、私がこれを使用して取得することを恐れていたメモリリークを説明しています
Norfeldt

6
@djaqeel:見積もりの​​後半はほぼ真実です。「静的データメンバーなど、アクティビティよりも長く存続するものにアクティビティコンテキストを与えないでください」とより適切に表現されています。ただし、特定の状況でそれが必要な理由正確にgetApplicationContext()わかっている場合にのみ使用します。レイアウトを膨らませる?アクティビティを使用します。サービスへのバインド。構成変更を存続させるためにそのバインドが必要な場合は、を使用して、バインディングがインスタンスに関連付けられないようにします。getApplicationContext()Activity
CommonsWare 2013年

7
@Sever:私は私の答えでこれをカバーします。Dave Smithは、コンテキストをカバーする優れたブログ投稿も提供しています:doubleencore.com/2013/06/context彼の要約段落:「ほとんどの場合、作業中の囲んでいるコンポーネントから直接利用できるコンテキストを使用します。参照がそのコンポーネントのライフサイクルを超えない限り、その参照への参照。アクティビティまたはサービスを超えて存在するオブジェクトからコンテキストへの参照を保存する必要があるとすぐに、その参照を切り替えて保存しますアプリケーションのコンテキストに移ります。」
CommonsWare 2013年

48

SDKサイトには十分に文書化されていないものがたくさんあると思いますが、これはその1つです。私がするつもりの主張は、アプリケーションコンテキストを使用するようにデフォルト設定し、本当に必要な場合にのみアクティビティコンテキストを使用するほうがよいように見えることです。アクティビティコンテキストが必要であると私が見た唯一の場所は、進行状況ダイアログ用です。SBERG412は、トーストメッセージにはアクティビティコンテキストを使用する必要があると主張していますが、Androidドキュメントには、使用されているアプリケーションコンテキストが明確に示されています。このGoogleの例のため、トーストには常にアプリケーションコンテキストを使用しました。そうするのが間違っている場合、Googleはここにボールを落としました。

考えて確認するべきことは次のとおりです。

トーストメッセージの場合、Google Dev Guideはアプリケーションコンテキストを使用し、それを使用することを明示的に言います: トースト通知

開発ガイドのダイアログセクションでは、AlertDialog.Builderがアプリケーションコンテキストを使用し、進行状況バーがアクティビティコンテキストを使用していることがわかります。これはGoogleでは説明されていません。 ダイアログ

アプリケーションコンテキストを使用するのは、向きの変更などの構成変更を処理したい場合や、ビューなどのコンテキストが必要なオブジェクトを保持したい場合に適しています。ここを見ると:実行時間の変更 リークを作成する可能性があるアクティビティコンテキストの使用に関する注意があります。これは、保持されるビューを持つアプリケーションコンテキストで回避できます(少なくともそれは私の理解です)。私が書いているアプリでは、方向の変更に関するいくつかのビューやその他のものを保持しようとしているため、アプリケーションコンテキストを使用するつもりです。したがって、メモリリークを引き起こさないようにアプリコンテキストを使用する必要があります(メモリリークの回避を参照))。私には、アクティビティコンテキストの代わりにアプリケーションコンテキストを使用するのに十分な理由があるように思われ、アクティビティコンテキストよりも頻繁に使用するように思えます。これは、私がこれまでに経験した多くのAndroidの本のようで、Googleの例の多くがそうしています。

Googleのドキュメントでは、ほとんどの場合、アプリケーションコンテキストの使用が完全に問題ないように見えますが、実際には、例(少なくとも私が見た例)でアクティビティコンテキストを使用するよりも頻繁に表示されます。アプリケーションコンテキストを使用することが本当にそのような問題である場合、Googleはこれをさらに強調する必要があります。彼らはそれを明確にする必要があり、彼らは彼らの例のいくつかをやり直す必要があります。権限(Google)がアプリケーションコンテキストを使用することは問題ではないように見えるので、私はこれを未経験の開発者に完全に非難することはしません。


5
同意します。CommonsWareの回答は、ちょっとした驚きでした。GoogleのドキュメントでgetApplicationContextの使用は非常に危険であると示唆されているため、この質問を見つけてよかったです。
Steve Schwarcz、2014年

38

この表は、アプリケーションコンテキスト(例:)getApplicationContext()アクティビティコンテキストBroadcastReceiverコンテキストなど、さまざまなタイプのコンテキストをいつ使用するかについてのガイダンスとして使用しました。

ここに画像の説明を入力してください

詳細については、すべてのメリットを原作者のここに紹介します。


11

使用するコンテキストは?

コンテキストには2つのタイプがあります。

  1. アプリケーションコンテキストアプリケーションに関連付けられており、アプリケーションの存続期間を通じて常に同じです。変更されません。したがって、トーストを使用している場合は、アプリケーションコンテキストまたはアクティビティコンテキスト(両方)を使用できます。トーストは、アプリケーション内のどこからでも表示でき、特定のウィンドウにアタッチされていないためです。ただし、多くの例外があり、1つはアクティビティコンテキストを使用または渡す必要がある場合です。

  2. アクティビティコンテキストアクティビティに関連付けられており、アクティビティが破棄されると破棄される可能性があります。単一のアプリケーションで複数のアクティビティが(おそらく)存在する可能性があります。また、アクティビティコンテキストハンドルが絶対に必要な場合もあります。たとえば、新しいアクティビティを起動する場合は、そのインテントでアクティビティコンテキストを使用して、新しい起動アクティビティがアクティビティスタックの観点から現在のアクティビティに接続されるようにする必要があります。ただし、アプリケーションのコンテキストを使用して新しいアクティビティを起動することもできますが、その場合Intent.FLAG_ACTIVITY_NEW_TASKは新しいタスクとして扱うためにフラグを設定する必要があります。

いくつかのケースを考えてみましょう:

  • MainActivity.this Activityクラスを拡張するMainActivityコンテキストを参照しますが、基本クラス(アクティビティ)もContextクラスを拡張するため、アクティビティコンテキストを提供するために使用できます。

  • getBaseContext() アクティビティコンテキストを提供します。

  • getApplication() アプリケーションコンテキストを提供します。

  • getApplicationContext() アプリケーションコンテキストも提供します。

詳細については、このリンクを確認してください


アプリにAlertDialogを表示する必要がある場合はどうでしょうか。たとえば、非同期プロセスが結果を表示します。この例としては、ユーザーがダウンロードをクリックすると、のダウンロードリクエストが発生しdownloadmanager、終了信号が受信されると、「このダウンロードで何を行いますか?」などのダイアログが表示されます。私の(ハック)ソリューションActivitystatic Applicationクラスの最新のものを保存しActivity、ダウンロードが完了すると現在のものを要求します。しかし、これが適切な実装であるとは思えません。TL; DRアプリのどこかにAlertDialogを表示する方法は?
CybeX 2018年

@KGCybeXダウンロードが完了したときにアプリのどこにでも表示したい場合は、ダウンロードサービスがブロードキャストする特定のメッセージをリッスンするアクティビティにブロードキャストレシーバーを手動で登録し、メッセージの受信時に必要な処理を行うか、添付する必要があります。そのサービスへのあなたの活動。
ExiRouS

6

それがサポートするすべての操作にアプリケーションコンテキストを使用しないのはなぜだろうと思いました。最終的には、メモリリークやgetContext()またはgetActivity()のnullチェックの欠落の可能性が低くなります(注入されたアプリケーションコンテキストを使用する場合、またはアプリケーションから静的メソッドを介して取得する場合)。Hackborn氏のように、必要な場合にのみアプリケーションコンテキストを使用するステートメントのように、理由の説明がないと説得力がないようです。しかし、なぜ私は失礼を見つけたようです:

一部のAndroidバージョン/デバイスの組み合わせには、これらのルールに従わない問題があることがわかりました。たとえば、コンテキストが渡されるBroadcastReceiverがあり、そのコンテキストをアプリケーションコンテキストに変換してから、アプリケーションコンテキストでregisterReceiver()を呼び出そうとすると、これが正常に機能するインスタンスが多数ありますが、 ReceiverCallNotAllowedExceptionによるクラッシュ。これらのクラッシュは、API 15から22までの幅広いAndroidバージョンで発生し ます。https://possiblemobile.com/2013/06/context/#comment-2443283153

下の表のアプリケーションコンテキストでサポートされていると説明されているすべての操作がすべてのAndroidデバイスで機能することは保証されていません。 ここに画像の説明を入力してください


4

アプリケーションコンテキストを使用すると例外が発生するため、アクティビティコンテキストとアプリケーションコンテキストを使用する必要がある2つの素晴らしい例は、トーストメッセージまたは組み込みのダイアログメッセージを表示する場合です。

ProgressDialog.show(this, ....);

または

Toast t = Toast.makeText(this,....);

これらはどちらも、アプリケーションコンテキストでは提供されないアクティビティコンテキストからの情報を必要とします。


5
Hm ..どのAndroid OSバージョンをテストしましたか?4.4.4でテストしましたが、問題なく動作します。さらに、@ Andi Jayが述べたように、公式のAndroid開発者ドキュメントでは、サンプルコードでアプリケーションコンテキストを使用しています。developer.android.com/guide/topics/ui/notifiers/...
김 준 호

1
@中国の名前、はい、それはうまくいくかもしれませんが、いつかそのアプリの将来的には、それもクラッシュします。私に何度か起こった。
Ojonugwa Jude Ochalifu

1
Toastでアクティビティコンテキストを使用すると、メモリリークが発生します。
Jemshit Iskenderov

3

アプリケーションコンテキストのライブそれまではアプリケーションが生きているだけであり、それは活動のライフサイクルに依存していないが、コンテキストキープオブジェクトは長寿命。一時的に使用するオブジェクトの場合、その時はアプリケーションコンテキストを使用し、アクティビティコンテキストは完全にアプリケーションコンテキストとは反対に使用されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.