Melempar dialog "Tidak dapat menambahkan jendela - token null bukan untuk aplikasi" dengan getApplication () sebagai konteks

665

Aktivitas saya sedang mencoba membuat AlertDialog yang memerlukan Konteks sebagai parameter. Ini berfungsi seperti yang diharapkan jika saya menggunakan:

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

Namun, saya ragu menggunakan "ini" sebagai konteks karena potensi kebocoran memori ketika Aktivitas dihancurkan dan diciptakan kembali bahkan selama sesuatu yang sederhana seperti rotasi layar. Dari pos terkait di blog pengembang Android :

Ada dua cara mudah untuk menghindari kebocoran memori terkait konteks. Yang paling jelas adalah menghindari keluar dari konteks di luar ruang lingkupnya sendiri. Contoh di atas menunjukkan kasus referensi statis tetapi kelas dalam dan referensi implisit mereka ke kelas luar bisa sama-sama berbahaya. Solusi kedua adalah dengan menggunakan konteks Aplikasi. Konteks ini akan hidup selama aplikasi Anda hidup dan tidak tergantung pada siklus hidup kegiatan. Jika Anda berencana untuk menjaga objek berumur panjang yang membutuhkan konteks, ingat objek aplikasi. Anda dapat memperolehnya dengan mudah dengan menghubungi Context.getApplicationContext () atau Activity.getApplication ().

Tetapi untuk AlertDialog()keduanya getApplicationContext()atau getApplication()dapat diterima sebagai Konteks, karena melemparkan pengecualian:

"Tidak dapat menambahkan jendela - token null bukan untuk aplikasi"

per referensi: 1 , 2 , 3 , dll.

Jadi, haruskah ini benar-benar dianggap sebagai "bug", karena kita secara resmi dinasihati untuk menggunakan Activity.getApplication()dan tidak berfungsi seperti yang diiklankan?

Jim

sepatu olahraga
sumber

Jawaban:

1354

Alih-alih getApplicationContext(), gunakan saja ActivityName.this.

Steven L.
sumber
67
Bagus! Hanya untuk mengomentari itu .. Anda kadang-kadang mungkin perlu menyimpan "ini" secara global, (misalnya) untuk mengaksesnya dalam metode yang diterapkan pendengar yang memiliki 'ini' sendiri. Dalam hal ini, Anda akan mendefinisikan "Konteks konteks" secara global, dan kemudian di onCreate, atur "context = this", dan kemudian merujuk ke "konteks". Harapan yang berguna juga.
Steven L
8
Sebenarnya, karena Listenerkelas sering anonim-batin, saya cenderung hanya melakukan final Context ctx = this;dan saya pergi;)
Alex
28
@ SvenvenL Untuk melakukan apa yang Anda katakan, Anda harus menggunakan ExternalClassName.ini untuk secara eksplisit merujuk ke "ini" dari kelas luar.
Artem Russakovskii
11
Tidak akan menggunakan "ini" membocorkannya jika dialog Anda digunakan dalam panggilan balik dan Anda meninggalkan aktivitas sebelum panggilan balik dipanggil? Setidaknya itulah yang tampaknya dikeluhkan oleh Android di logcat.
Artem Russakovskii
6
Saya tidak akan menyarankan pendekatan @StevenLs karena Anda dapat dengan mudah membocorkan memori aktivitas itu kecuali Anda ingat untuk menghapus referensi statis di onDestroy - Artem benar. Pendekatan StevenLs berasal dari kurangnya pemahaman tentang bagaimana Java bekerja
Dori
192

Menggunakan thistidak berhasil untuk saya, tetapi MyActivityName.thisberhasil. Semoga ini bisa membantu siapa saja yang tidak bisa mulai thisbekerja.

TrueCoke
sumber
63
Itulah yang terjadi ketika Anda menggunakan thisdari dalam kelas batin. Jika Anda ingin referensi instance kelas luar, Anda harus menentukannya, seperti yang Anda lakukan dengan OuterClass.this. Hanya menggunakan thisselalu referensi instance kelas paling dalam.
kaka
60

Anda dapat terus menggunakan getApplicationContext(), tetapi sebelum digunakan, Anda harus menambahkan tanda ini:, dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)dan kesalahan tidak akan ditampilkan.

Tambahkan izin berikut ke manifes Anda:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
codezjx
sumber
1
Saya tidak dapat menambahkan jendela android.view.ViewRootImpl$W@426ce670 - izin ditolak untuk jenis jendela ini
Ram G.
tambahkan izin: <menggunakan-izin android: name = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx
3
Sepertinya Anda tidak dapat mengaktifkan izin ini di API 23 dan seterusnya code.google.com/p/android-developer-preview/issues/…
roy zhang
1
Anda dapat menggunakannya untuk API 23 dan seterusnya, namun Anda harus meminta pengguna: startActivityForResult (Maksud baru (Pengaturan.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("paket:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); Namun, apakah Anda harus menggunakannya adalah masalah lain ...
Ben Neill
2
Ini berguna ketika Anda menunjukkan dialog kemajuan di dalam layanan
Anand Savjani
37

Anda telah mengidentifikasi masalah dengan benar ketika Anda mengatakan "... untuk AlertDialog () tidak getApplicationContext () atau getApplication () dapat diterima sebagai suatu Konteks, karena melemparkan pengecualian: 'Tidak dapat menambahkan jendela - token null bukan untuk sebuah aplikasi'"

Untuk membuat Dialog, Anda memerlukan Konteks Aktivitas atau Konteks Layanan , bukan Konteks Aplikasi (keduanya getApplicationContext () dan getApplication () mengembalikan Konteks Aplikasi).

Inilah cara Anda mendapatkan Konteks Aktivitas :

(1) Dalam Suatu Kegiatan atau Layanan:

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

(2) Dalam Fragmen: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Kebocoran memori bukan masalah yang intrinsik dengan referensi "ini", yang merupakan referensi objek untuk dirinya sendiri (yaitu referensi ke memori yang dialokasikan aktual untuk menyimpan data objek). Hal ini terjadi untuk setiap memori yang dialokasikan untuk yang Kolektor Sampah (GC) tidak dapat membebaskan setelah memori yang dialokasikan telah hidup lebih lama umur pakainya.

Sebagian besar waktu, ketika suatu variabel keluar dari ruang lingkup, memori akan direklamasi oleh GC. Namun, kebocoran memori dapat terjadi ketika referensi ke suatu objek yang dipegang oleh variabel, katakanlah "x", tetap ada bahkan setelah objek telah hidup lebih lama. Memori yang dialokasikan karenanya akan hilang selama "x" menyimpan referensi untuk itu karena GC tidak akan membebaskan memori selama memori itu masih dirujuk. Terkadang, kebocoran memori tidak terlihat karena rangkaian referensi ke memori yang dialokasikan. Dalam kasus seperti itu, GC tidak akan membebaskan memori sampai semua referensi ke memori tersebut telah dihapus.

Untuk mencegah kebocoran memori, periksa kode Anda untuk kesalahan logis yang menyebabkan memori yang dialokasikan direferensikan tanpa batas oleh "ini" (atau referensi lain). Ingatlah untuk memeriksa referensi rantai juga. Berikut adalah beberapa alat yang dapat Anda gunakan untuk membantu Anda menganalisis penggunaan memori dan menemukan kebocoran memori sial itu:

SATU
sumber
Untuk sebuah Aktivitas, Anda juga dapat menggunakan ActivityName.ini adalah ActivityName di mana (jelas) nama aktivitas Anda (misalnya MainActivity)
Luis Cabrera Benito
34

Dialog Anda seharusnya tidak menjadi "objek berumur panjang yang membutuhkan konteks". Dokumentasinya membingungkan. Pada dasarnya jika Anda melakukan sesuatu seperti:

static Dialog sDialog;

(perhatikan statis )

Kemudian dalam suatu kegiatan di suatu tempat Anda lakukan

 sDialog = new Dialog(this);

Anda mungkin akan membocorkan aktivitas asli selama rotasi atau serupa yang akan menghancurkan aktivitas. (Kecuali Anda membersihkan di onDestroy, tetapi dalam hal ini Anda mungkin tidak akan membuat objek Dialog statis)

Untuk beberapa struktur data masuk akal untuk membuatnya statis dan didasarkan pada konteks aplikasi, tetapi umumnya tidak untuk hal-hal terkait UI, seperti dialog. Jadi sesuatu seperti ini:

Dialog mDialog;

...

mDialog = new Dialog(this);

Tidak masalah dan tidak boleh membocorkan aktivitas karena mDialog akan dibebaskan dengan aktivitas karena tidak statis.

Kevin TeslaCoil
sumber
Saya memanggilnya dari asynctask, ini bekerja untuk saya, thx mate
MemLeak
dialog saya statis, setelah saya menghapus deklarasi statis itu berfungsi.
Ceddy Muhoza
25

Saya harus mengirim konteks melalui konstruktor pada adaptor khusus yang ditampilkan dalam fragmen dan mengalami masalah ini dengan getApplicationContext (). Saya menyelesaikannya dengan:

this.getActivity().getWindow().getContext()dalam onCreatepanggilan balik fragmen .

Grux
sumber
4
Ini bekerja untuk saya juga, saya meneruskannya ke konstruktor AsyncTask eksternal yang saya gunakan (Ini menunjukkan Dialog kemajuan).
Rohan Kandwal
3
ini adalah jawaban NYATA untuk tugas-tugas yang lebih kompleks :)
teejay
1
Saya setuju dengan @teejay
Erdi İzgi
23

dalam Aktivitas cukup gunakan:

MyActivity.this

dalam Fragmen:

getActivity();
Mahmoud Ayman
sumber
Ini memperbaikinya untuk saya di Aktivitas saya. Terima kasih
Werner
20

Di Activityklik tombol yang menunjukkan kotak dialog

Dialog dialog = new Dialog(MyActivity.this);

Bekerja untukku.

P_Pran
sumber
19

***** versi kotlin *****

Anda harus lulus this@YourActivityalih-alih applicationContextataubaseContext

MilaDroid
sumber
18

Sedikit hack: Anda dapat mencegah menghancurkan aktivitas Anda dengan GC (Anda tidak harus melakukannya, tetapi dapat membantu dalam beberapa situasi Jangan lupa untuk set. contextForDialogUntuk nullketika itu tidak lagi diperlukan):

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();
    }
    ...
}
Mikalai Daronin
sumber
@MurtuzaKabul Ini berfungsi karena ini == PostActivity yang mewarisi dari Activity-> yang mewarisi dari Konteks, jadi ketika Anda melewati dialog konteks Anda, Anda benar-benar melewati aktivitas
Elad Gelman
13

Jika Anda menggunakan fragmen dan menggunakan pesan AlertDialog / Toast kemudian gunakan getActivity () dalam parameter konteks.

seperti ini

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

Cukup gunakan berikut ini:

UNTUK PENGGUNA JAWA

Jika Anda menggunakan aktivitas -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

ATAU

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

Jika Anda menggunakan fragmen -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

UNTUK PENGGUNA KOTLIN

Jika Anda menggunakan aktivitas -> val builder = AlertDialog.Builder(this)

ATAU

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

Jika Anda menggunakan fragmen -> val builder = AlertDialog.Builder(activity!!)

Muhammad Faizan
sumber
9

menambahkan

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

dan

"android.permission.SYSTEM_ALERT_WINDOW"/> dalam manifes

Ini bekerja untuk saya sekarang. Setelah menutup dan membuka aplikasi, memberi saya kesalahan pada saat itu.

AlphaStack
sumber
9

Saya menggunakan ProgressDialogdalam sebuah fragmen dan mendapatkan kesalahan ini saat meneruskan getActivity().getApplicationContext()sebagai parameter konstruktor. Mengubahnya menjadi getActivity().getBaseContext()tidak berhasil juga.

Solusi yang berhasil bagi saya adalah lulus getActivity(); yaitu

progressDialog = new ProgressDialog(getActivity());

TM
sumber
6

Menggunakan MyDialog md = new MyDialog(MyActivity.this.getParent());

MSA
sumber
6

Jika Anda berada di luar Aktivitas maka Anda perlu menggunakan fungsi "NameOfMyActivity.this" ini sebagai aktivitas Aktivitas, contoh:

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);
oabareload
sumber
5

Jika Anda menggunakan fragmen dan menggunakan AlertDialog / Toastpesan, gunakangetActivity() dalam parameter konteks.

Bekerja untukku.

Bersulang!

curlyreggie
sumber
5

Cobalah untuk menggunakan konteks suatu kegiatan yang akan di bawah dialog. Tetapi berhati-hatilah ketika Anda menggunakan kata kunci "ini", karena itu tidak akan berfungsi setiap saat.

Sebagai contoh, jika Anda memiliki TabActivity sebagai host dengan dua tab, dan setiap tab adalah aktivitas lain, dan jika Anda mencoba membuat dialog dari salah satu tab (aktivitas) dan jika Anda menggunakan "ini", maka Anda akan mendapatkan pengecualian, dalam hal ini dialog kasus harus terhubung ke aktivitas host yang menampung semuanya dan terlihat. (Anda dapat mengatakan konteks aktivitas induk yang paling terlihat)

Saya tidak menemukan info ini dari dokumen apa pun tetapi dengan mencoba. Ini adalah solusi saya tanpa latar belakang yang kuat, Jika ada orang yang lebih dikenal, jangan ragu untuk berkomentar.

Engin OZTURK
sumber
4

Untuk pembaca masa depan, ini akan membantu:

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

Dalam kasus kerja saya:

this.getContext();
pejalan kaki
sumber
2

Atau kemungkinan lain adalah membuat Dialog sebagai berikut:

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));
Martin Koubek
sumber
2

Saya pikir itu mungkin terjadi juga jika Anda mencoba untuk menampilkan dialog dari utas yang bukan utas UI utama.

Gunakan runOnUiThread()dalam kasus itu.

Erwan
sumber
2

Coba getParent()di tempat argumen konteks seperti AlertDialog.Builder(getParent());Harapan baru itu akan berhasil, itu berhasil untuk saya.

Priyank Joshi
sumber
1

Setelah melihat API, Anda dapat meneruskan dialog aktivitas Anda atau getActivity jika Anda berada di sebuah fragmen, lalu dengan paksa membersihkannya dengan dialog.dismiss () dalam metode pengembalian untuk mencegah kebocoran.

Meskipun tidak secara eksplisit dinyatakan di mana pun saya tahu, sepertinya Anda meneruskan dialog di OnClickHandlers hanya untuk melakukan ini.

G_V
sumber
0

Jika Dialog Anda dibuat di adaptor:

Lewati Aktivitas ke Konstruktor Adaptor:

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

Terima pada Adaptor:

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

Sekarang Anda dapat menggunakan Builder Anda

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);
josedlujan
sumber
-1

Inilah cara saya mengatasi kesalahan yang sama untuk aplikasi saya:
Menambahkan baris berikut setelah membuat dialog:

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

Anda tidak perlu memperoleh konteks. Ini sangat berguna jika Anda memunculkan dialog lain dari dialog yang muncul saat ini. Atau ketika tidak nyaman untuk mendapatkan konteks.

Semoga ini bisa membantu Anda dengan pengembangan aplikasi Anda.

David

us_david
sumber
-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();
BumpBitcoin
sumber