getApplication()をコンテキストとして使用して、「ウィンドウを追加できません-トークンnullはアプリケーション用ではありません」をスローするダイアログ


665

私のアクティビティは、パラメータとしてコンテキストを必要とするAlertDialogを作成しようとしています。私が使用する場合、これは期待どおりに機能します:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

ただし、アクティビティが破棄されて画面の回転などの単純なものである場合でもメモリリークが発生する可能性があるため、「this」をコンテキストとして使用することはお勧めしません。Android開発者のブログの関連記事から:

コンテキスト関連のメモリリークを回避する簡単な方法が2つあります。最も明白なのは、コンテキストが自身のスコープ外にエスケープされないようにすることです。上記の例では、静的参照の場合を示しましたが、内部クラスとそれらの外部クラスへの暗黙的な参照も同様に危険です。2番目のソリューションは、アプリケーションコンテキストを使用することです。このコンテキストは、アプリケーションが有効であり、アクティビティのライフサイクルに依存しない限り有効です。コンテキストを必要とする長期間存続するオブジェクトを保持する予定の場合は、アプリケーションオブジェクトを覚えておいてください。Context.getApplicationContext()またはActivity.getApplication()を呼び出すことで簡単に取得できます。

しかし、のためにAlertDialog()どちらgetApplicationContext()getApplication()に例外をスローするように、コンテキストとして許容可能です:

「ウィンドウを追加できません—トークンnullはアプリケーション用ではありません」

:参照ごとに123、など

正式に使用することActivity.getApplication()をお勧めしますが、宣伝どおりに機能しないため、これは本当に「バグ」と考える必要がありますか?

ジム


R.GuyはgetApplicationを用いてアドバイス最初の項目のための基準: android-developers.blogspot.com/2009/01/...
gymshoe




回答:


1354

の代わりにgetApplicationContext()、を使用してくださいActivityName.this


67
すごい!それについてコメントするだけです。たとえば、独自の「this」を持っているリスナーの実装されたメソッド内でそれにアクセスするために、「this」をグローバルに格納する必要がある場合があります。その場合、「コンテキストコンテキスト」をグローバルに定義し、onCreateで「context = this」を設定してから、「context」を参照します。それも重宝すると思います。
Steven L

8
実際、Listenerクラスは多くの場合匿名の内部なので、私はただ行う傾向がありfinal Context ctx = this;、私は離れています;)
Alex

28
@StevenL言っていることを行うには、ExternalClassName.thisを使用して、外部クラスの「this」を明示的に参照する必要があります。
Artem Russakovskii、2011年

11
コールバックでダイアログが使用され、コールバックが呼び出される前にアクティビティを終了すると、「this」を使用してリークしませんか?少なくとも、Androidはlogcatで不満があるようです。
Artem Russakovskii、2011年

6
onDestroyで静的参照をクリアすることを忘れない限り、そのアクティビティのメモリを簡単にリークする可能性があるため、@ StevenLsのアプローチはお勧めしません-Artemは正しいです。StevenLsのアプローチは、Javaがどのように機能するかを理解の欠如からボーンれる
通り

192

使用thisは私にとってはうまくいきませんでしたが、実際にMyActivityName.thisはうまくいきました。これがthis仕事に行けなかった人を助けることを願っています。


63
これthisが、内部クラスの内部から使用した場合に起こります。外部クラスのインスタンスを参照する場合は、と同様に、それを指定する必要がありますOuterClass.this。を使用するだけで、this常に最も内側のクラスのインスタンスが参照されます。
カカ

60

を引き続き使用できますが、使用するgetApplicationContext()前に次のフラグを追加する必要dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)があります。そうすると、エラーは表示されません。

マニフェストに次の権限を追加します。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

1
ウィンドウandroid.view.ViewRootImpl$W@426ce670を追加できません-このウィンドウタイプのアクセス許可が拒否されました
Ram G.

権限を追加:<uses-permission android:name = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx

3
API 23以降ではこの権限を有効にできないようです。code.google.com/ p / android
roy zhang

1
API 23以降でも使用できますが、ユーザーにプロンプ​​トを表示する必要があります。しかし、あなたがするかどうかをする必要があり、それはまた別の問題である使用して...
ベン・ニール

2
これは、サービス内で進行状況ダイアログを表示する場合に役立ちます
Anand Savjani

37

「... AlertDialog()の場合、getApplicationContext()もgetApplication()も例外としてスローされるため、コンテキストとしては受け入れられません。アプリケーション'"

ダイアログを作成するには、アプリケーションコンテキストではなく、アクティビティコンテキストまたはサービスコンテキストが必要です(getApplicationContext()とgetApplication()の両方がアプリケーションコンテキストを返します)。

アクティビティコンテキストを取得する方法は次のとおりです。

(1)アクティビティまたはサービス内:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

(2)フラグメント内: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

メモリリークは、オブジェクトのそれ自体への参照(つまり、オブジェクトのデータを格納するために実際に割り当てられたメモリへの参照)である「this」参照に固有の問題ではありません。これは、割り当てられたメモリが有効寿命を超えた後、ガベージコレクタ(GC)が解放できない割り当てられたメモリで発生します

ほとんどの場合、変数がスコープ外になると、メモリはGCによって回収されます。ただし、メモリリークは、変数によって保持されているオブジェクトへの参照(「x」など)がオブジェクトの有効寿命を超えた後も持続する場合に発生する可能性があります。したがって、「x」が参照を保持している限り、割り当てられたメモリは失われます。これは、GC そのメモリがまだ参照されている限り、メモリを解放しないためです。割り当てられたメモリへの一連の参照が原因で、メモリリークが明確にならない場合があります。そのような場合、そのメモリへのすべての参照が削除されるまで、GCはメモリを解放しません。

メモリリークを防ぐには、割り当てられたメモリが「this」(またはその他の参照)によって無期限に参照される原因となる論理エラーがないか、コードを確認してください。チェーンの参照もチェックすることを忘れないでください。ここでは、メモリの使用状況を分析し、厄介なメモリリークを見つけるのに役立つツールをいくつか紹介します。


活動のために使うこともできActivityName.this ActivityNameは(明らかに)(例えばMainActivity用)アクティビティの名前である
ルイス・カブレラベニート

34

ダイアログは、「コンテキストを必要とする長期間有効なオブジェクト」であってはなりません。ドキュメントは紛らわしいです。基本的にあなたが次のようなことをした場合:

static Dialog sDialog;

静的に注意してください)

次に、どこかで行ったアクティビティで

 sDialog = new Dialog(this);

ローテーションなどでアクティビティを破壊するようなオリジナルのアクティビティをリークしている可能性があります。(onDestroyでクリーンアップしない限り、その場合はおそらくDialogオブジェクトを静的にしないでしょう)

一部のデータ構造では、それらを静的にし、アプリケーションのコンテキストに基づくことが理にかなっていますが、通常、ダイアログなどのUI関連のものには適していません。だからこのようなもの:

Dialog mDialog;

...

mDialog = new Dialog(this);

mDialogは静的ではないため、アクティビティで解放されるため、問題なく、アクティビティをリークしないでください。


私は、これは私のために働いた、asynctaskからそれを呼び出して、THXメイトだ
memleakの

私のダイアログは静的でしたが、静的宣言を削除すると機能しました。
Ceddy Muhoza 2016年

25

フラグメントに表示されたカスタムアダプターのコンストラクターを介してコンテキストを送信する必要があり、getApplicationContext()でこの問題が発生しました。私はそれを解決しました:

this.getActivity().getWindow().getContext()フラグメントのonCreateコールバックで。


4
これも私にとってはうまくいきました。使用している外部AsyncTaskのコンストラクターに渡しました(進行状況ダイアログが表示されます)。
Rohan Kandwal 2014年

3
これは、より複雑なタスクの
実際の

1
私は@teejayに同意します
Erdiİzgi


20

Activityダイアログボックスを表示ボタンをクリックするだけで

Dialog dialog = new Dialog(MyActivity.this);

私のために働いた。


19

***** Kotlinバージョン*****

またはのthis@YourActivity代わりに渡す必要がありますapplicationContextbaseContext


18

ちょっとしたハック:GCによってアクティビティが破壊されるのを防ぐことができます(そうするべきではありませんが、状況によっては役立ちます。不要になったときに設定contextForDialogすることを忘れないでくださいnull):

public class PostActivity extends Activity  {
    ...
    private Context contextForDialog = null;
    ...
    public void onCreate(Bundle savedInstanceState) {
        ...
        contextForDialog = this;
    }
    ...
    private void showAnimatedDialog() {
        mSpinner = new Dialog(contextForDialog);
        mSpinner.setContentView(new MySpinner(contextForDialog));
        mSpinner.show();
    }
    ...
}

@MurtuzaKabul動作するのは、これが==アクティビティから継承するPostActivity->コンテキストから継承するため、ダイアログを渡すと、コンテキストが実際にアクティビティを渡すことになります
Elad Gelman 2013

13

フラグメントを使用していてAlertDialog / Toastメッセージを使用している場合は、コンテキストパラメータでgetActivity()を使用します。

このような

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();

12

以下を使用してください:

JAVAユーザー向け

アクティビティを使用している場合-> AlertDialog.Builder builder = new AlertDialog.Builder(this);

または

AlertDialog.Builder builder = new AlertDialog.Builder(your_activity.this);

フラグメントを使用している場合-> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

KOTLINユーザー向け

アクティビティを使用している場合-> val builder = AlertDialog.Builder(this)

または

val builder = AlertDialog.Builder(this@your_activity.this)

フラグメントを使用している場合-> val builder = AlertDialog.Builder(activity!!)


9

追加

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

そして

"android.permission.SYSTEM_ALERT_WINDOW"/> マニフェストで

それは今私のために動作します。アプリケーションを閉じて開いた後でも、そのときエラーが発生しました。


9

私はProgressDialogフラグメントで使用していgetActivity().getApplicationContext()て、コンストラクターのパラメーターとして渡すとこのエラーが発生しました。に変更しても機能getActivity().getBaseContext()しませんでした。

私のために働いた解決策は合格することgetActivity()でした。すなわち

progressDialog = new ProgressDialog(getActivity());


6

使用する MyDialog md = new MyDialog(MyActivity.this.getParent());


6

アクティビティの外にいる場合は、関数「NameOfMyActivity.this」でアクティビティアクティビティとして使用する必要があります。例:

public static void showDialog(Activity activity) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setMessage("Your Message")
        .setPositiveButton("Yes", dialogClickListener)
        .setNegativeButton("No", dialogClickListener).show();
}


//Outside your Activity
showDialog(NameOfMyActivity.this);

5

フラグメントとAlertDialog / Toastメッセージを使用getActivity()している場合は、コンテキストパラメータで使用します。

私のために働いた。

乾杯!


5

ダイアログの下にあるアクティビティのコンテキストを使用してみてください。ただし、「this」キーワードを使用するときは、常に機能するとは限らないので注意してください。

たとえば、2つのタブを持つホストとしてTabActivityがあり、各タブが別のアクティビティであり、いずれかのタブ(アクティビティ)からダイアログを作成しようとして「this」を使用すると、例外が発生します。ケースダイアログは、すべてをホストし、表示されるホストアクティビティに接続する必要があります。(最も目に見える親アクティビティのコンテキストを言うことができます)

この情報はどのドキュメントからも見つかりませんでしたが、試しました。これは、強い背景のない私の解決策です。もし知識が豊富な人がいたら、コメントしてください。


4

将来の読者にとって、これは役立つでしょう:

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}


2

または、次のようにダイアログを作成することもできます。

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));

2

メインのUIスレッドではないスレッドからダイアログを表示しようとした場合にも、同様のことが起こると思います。

runOnUiThread()その場合に使用します。


2

getParent()新しいAlertDialog.Builder(getParent());期待が機能するように、コンテキストの議論の場所で試してみてください。


1

APIを確認したら、アクティビティにダイアログを渡すか、フラグメントの場合はgetActivityを渡し、returnメソッドでdialog.dismiss()を使用して強制的にクリーンアップして、リークを防止できます。

私が知っているどこにも明示的には記載されていませんが、これを行うためにOnClickHandlersのダイアログに戻されているようです。


0

ダイアログがアダプターで作成されている場合:

アクティビティをアダプタコンストラクタに渡します。

adapter = new MyAdapter(getActivity(),data);

アダプターで受け取る:

 public MyAdapter(Activity activity, List<Data> dataList){
       this.activity = activity;
    }

これでビルダーで使用できます

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);

-1

これが、アプリケーションで同じエラーを解決した方法です。
ダイアログの作成後に次の行を追加します。

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);  

コンテキストを取得する必要はありません。これは、現在ポップアップしているダイアログの上に別のダイアログをポップアップする場合に特に便利です。または、コンテキストを取得するのが不都合な場合。

これがアプリ開発に役立つことを願っています。

デビッド


-1
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getWindow().getDecorView().getRootView().getContext());

builder.setTitle("Confirm");
builder.setMessage("Are you sure you want delete your old account?");

builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
        //Do nothing but close the dialog



        dialog.dismiss();

    }
});

builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

        //Do nothing
        dialog.dismiss();
    }
});

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