“Android.view.WindowManager $ BadTokenException: Tidak dapat menambahkan jendela” di buider.show ()

120

Dari utama saya activity, saya perlu memanggil kelas dalam dan dalam metode di dalam kelas, saya perlu menunjukkan AlertDialog. Setelah menutupnya, saat tombol OK ditekan, teruskan ke Google Play untuk membeli.

Segala sesuatunya bekerja dengan sempurna untuk sebagian besar waktu, tetapi untuk beberapa pengguna ini macet builder.show()dan saya dapat melihat "android.view.WindowManager$BadTokenException:Tidak dapat menambahkan jendela "dari log kerusakan. Harap sarankan.

Kode saya kurang lebih seperti ini:

public class classname1 extends Activity{

  public void onCreate(Bundle savedInstanceState) {
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.<view>); 

    //call the <className1> class to execute
  }

  private class classNamename2 extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {}

    protected void onPostExecute(String result){
      if(page.contains("error")) 
      {
        AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
        builder.setCancelable(true);
        builder.setMessage("");
        builder.setInverseBackgroundForced(true);
        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
            if(!<condition>)
            {
              try
              {
                String pl = ""; 

                mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                  <listener>, pl);
              }

              catch(Exception e)
              {
                e.printStackTrace();
              }
            }  
          }
        });

        builder.show();
      }
    }
  }
}

Saya juga telah melihat kesalahan di lansiran lain di mana saya tidak meneruskan ke yang lain activity. Sederhana seperti ini:

AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
    builder.setCancelable(true);

    //if successful
    builder.setMessage(" ");
    builder.setInverseBackgroundForced(true);
    builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton){
            // dialog.dismiss();
                   }
    });
    builder.show();
}
MSIslam
sumber
2
Jika ini adalah kode lengkap Anda, apakah Anda benar-benar memerlukan AsyncTask?
Shobhit Puri
Ini bukan kode lengkapnya, ini adalah kode yang cukup besar jadi saya hanya menambahkan bagian di sini di mana saya melihat masalah dari laporan kerusakan
MSIslam
oke bagus. Biasanya Anda hanya dapat memposting nama fungsi dan komentar bahwa Anda melakukan banyak hal di sana (seperti yang Anda lakukan sekarang). Lebih mudah dimengerti. :).
Shobhit Puri
Apakah Anda menavigasi ke aktivitas lain dari aktivitas ini di suatu tempat?
Shobhit Puri
1
Anda telah menulis adalah komentar //send to some other activity. Saya pikir jika Anda akan mengomentari bagian di mana Anda akan pergi ke Aktivitas baru, kesalahan ini akan hilang. Kesalahan ini tampaknya terjadi karena dialog sebelum Anda ditutup sepenuhnya, aktivitas baru Anda dimulai. Di onPostExecute(), Anda memiliki dialog peringatan dan Anda memberikan konteks loginAktivitas. Tapi Anda menavigasi ke aktivitas lain, jadi konteksnya menjadi salah. Karenanya Anda mendapatkan kesalahan ini! Lihat stackoverflow.com/questions/15104677/… pertanyaan serupa.
Shobhit Puri

Jawaban:

266
android.view.WindowManager$BadTokenException: Unable to add window"

Masalah:

Pengecualian ini terjadi saat aplikasi mencoba memberi tahu pengguna dari thread latar belakang (AsyncTask) dengan membuka Dialog.

Jika Anda mencoba memodifikasi UI dari thread latar belakang (biasanya dari onPostExecute () AsyncTask) dan jika aktivitas memasuki tahap penyelesaian yaitu) secara eksplisit memanggil finish (), pengguna menekan tombol home atau back atau pembersihan aktivitas yang dibuat oleh Android maka Anda dapatkan kesalahan ini.

Alasan:

Alasan untuk pengecualian ini adalah, seperti yang dikatakan dalam pesan pengecualian, aktivitas telah selesai tetapi Anda mencoba menampilkan dialog dengan konteks aktivitas yang sudah selesai. Karena tidak ada jendela untuk dialog yang menampilkan runtime android akan memunculkan pengecualian ini.

Larutan:

Gunakan isFinishing()metode yang dipanggil oleh Android untuk memeriksa apakah aktivitas ini sedang dalam proses penyelesaian: apakah itu panggilan finish () eksplisit atau pembersihan aktivitas yang dibuat oleh Android. Dengan menggunakan metode ini, sangat mudah untuk menghindari dialog pembuka dari thread latar belakang saat aktivitas selesai.

Juga pertahankan a weak referenceuntuk aktivitas (dan bukan referensi yang kuat sehingga aktivitas bisa dimusnahkan jika tidak diperlukan) dan periksa apakah aktivitas belum selesai sebelum menjalankan UI apa pun menggunakan referensi aktivitas ini (yaitu menampilkan dialog).

mis .

private class chkSubscription extends AsyncTask<String, Void, String>{

  private final WeakReference<login> loginActivityWeakRef;

  public chkSubscription (login loginActivity) {
    super();
    this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
  }

  protected String doInBackground(String... params) {
    //web service call
  }

  protected void onPostExecute(String result) {
    if(page.contains("error")) //when not subscribed
    {
      if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
        AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
        builder.setCancelable(true);
        builder.setMessage(sucObject);
        builder.setInverseBackgroundForced(true);

        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
          }
        });

        builder.show();
      }
    }
  }
}

Pembaruan:

Token Jendela:

Seperti yang tersirat dari namanya, token jendela adalah tipe khusus dari Binder token yang digunakan pengelola jendela untuk mengidentifikasi jendela secara unik dalam sistem. Token jendela penting untuk keamanan karena tidak memungkinkan aplikasi hasad untuk menarik jendela aplikasi lain di atas. Manajer jendela melindungi terhadap hal ini dengan meminta aplikasi untuk meneruskan token jendela aplikasi mereka sebagai bagian dari setiap permintaan untuk menambah atau menghapus jendela. Jika token tidak cocok, pengelola jendela menolak permintaan dan menampilkan BadTokenException . Tanpa token jendela, langkah identifikasi yang diperlukan ini tidak akan mungkin dilakukan dan pengelola jendela tidak akan dapat melindungi dirinya sendiri dari aplikasi berbahaya.

 Skenario dunia nyata:

Saat aplikasi dimulai untuk pertama kali,  ActivityManagerService  membuat jenis token jendela khusus yang disebut token jendela aplikasi, yang secara unik mengidentifikasi jendela container tingkat atas aplikasi. Manajer aktivitas memberikan token ini ke aplikasi dan pengelola jendela, dan aplikasi mengirimkan token ke pengelola jendela setiap kali ingin menambahkan jendela baru ke layar. Ini memastikan interaksi yang aman antara aplikasi dan pengelola jendela (dengan membuatnya tidak mungkin untuk menambahkan jendela di atas aplikasi lain), dan juga memudahkan pengelola aktivitas untuk membuat permintaan langsung ke pengelola jendela.

Ritesh Gune
sumber
Ini sangat masuk akal! Juga solusi Anda terdengar bagus bagi saya. (y)
MSIslam
'The blank final field loginActivityWeakRef mungkin belum diinisialisasi' dan dicoba dengan cara ini: private final WeakReference <login> loginActivityWeakRef = new WeakReference <login> (login.this); tidak yakin apakah itu hal yang benar untuk dilakukan
MSIslam
Juga saya menghapus final sebelum WeakReference <login> loginActivityWeakRef karena itu menunjukkan kesalahan dalam konstruktor.
MSIslam
1
coba gunakan chkCubscription baru (this) .execute (""); alih-alih chkCubscription.execute ("") baru; seperti yang Anda posting di atas.
Ritesh Gune
2
Kesalahan yang mengerikan !! Saya mengikuti tutorial dan sebagai @PhilRoggenbuck, masalah saya disebabkan oleh pemanggilan Toast..Show () sebelum memanggil StartActivity (...). Untuk memperbaikinya, saya memindahkan roti panggang di aktivitas yang baru dipanggil!
Thierry
27

Saya memiliki dialog yang menampilkan fungsi:

void showDialog(){
    new AlertDialog.Builder(MyActivity.this)
    ...
    .show();
}

Saya mendapatkan kesalahan ini dan saya hanya perlu memeriksa isFinishing()sebelum memanggil fungsi yang menampilkan dialog ini.

if(!isFinishing())
    showDialog();
Jemshit Iskenderov
sumber
1
bukankah kita harus menulis if(!MyActivity.this.isFinishing())? Jika tidak benar di MyActivity
Bibaswann Bandyopadhyay
2
Mengapa Android menjalankan kode apa pun jika sudah selesai? Jika kita mengikuti solusi ini, maka bayangkan berapa banyak waktu kita harus benar-benar menggunakan isFinishing untuk menghindari masalah serupa.
David
@David Saya pikir ini kehilangan beberapa detail, seperti dialog yang dipanggil di utas latar belakang, tetapi saya sepenuhnya setuju dengan maksud Anda seperti sekarang.
Keranjang Terbengkalai
Poin yang bagus, mengapa sih saya perlu memeriksa isFinishing!
Chibueze Opata
9

Alasan yang mungkin adalah konteks dialog peringatan. Anda mungkin menyelesaikan aktivitas itu sehingga mencoba untuk membuka dalam konteks itu tetapi yang sudah ditutup. Coba ubah konteks dialog itu menjadi aktivitas pertama Anda karena tidak akan selesai sampai akhir.

misalnya

daripada ini.

AlertDialog alertDialog = new AlertDialog.Builder(this).create();

coba gunakan

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
Raghavendra
sumber
3
  • pertama, Anda tidak dapat memperluas AsyncTask tanpa mengganti doInBackground
  • kedua, coba buat AlterDailog dari pembangun lalu panggil show ().

    private boolean visible = false;
    class chkSubscription extends AsyncTask<String, Void, String>
    {
    
        protected void onPostExecute(String result)
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setCancelable(true);
            builder.setMessage(sucObject);
            builder.setInverseBackgroundForced(true);
            builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton)
                {
                    dialog.dismiss();
                }
            });
    
            AlertDialog myAlertDialog = builder.create();
            if(visible) myAlertDialog.show();
        }
    
        @Override
        protected String doInBackground(String... arg0)
        {
            // TODO Auto-generated method stub
            return null;
        }
    }
    
    
    @Override
    protected void onResume()
    {
        // TODO Auto-generated method stub
        super.onResume();
        visible = true;
    }
    
    @Override
    protected void onStop()
    {
        visible = false; 
        super.onStop();
    }
moh.sukhni
sumber
1
Terima kasih atas jawabannya. Saya sebenarnya menggunakan metode doInBackground, hanya tidak menyebutkannya di sini karena tidak terkait dengan peringatan. Mengenai menambahkan builder.create (), tampaknya berfungsi dengan baik, tetapi tidak tahu apakah itu akan berfungsi dengan baik untuk semua orang. Seperti yang saya katakan sebelumnya, kode saya saat ini juga berfungsi dengan baik, tetapi hanya beberapa kali untuk beberapa pengguna itu menunjukkan tidak dapat menambahkan masalah jendela. Bisakah Anda memberi tahu saya apa yang sebenarnya menjadi masalah dalam pengkodean saya apa yang mungkin menyebabkan ini?
MSIslam
dalam hal ini pengguna keluar dari aktivitas Anda sebelum onPostExecute dipanggil, sehingga tidak ada jendela untuk menahan dialog, dan ini menyebabkan aplikasi Anda mogok. tambahkan bendera ke onStop untuk mengetahui jika aktivitas Anda tidak lagi terlihat, jangan tampilkan dialog.
moh.sukhni
onPostExecute sebenarnya dipanggil, karena builder.show () berada dalam kondisi ketika saya memeriksa apakah pengguna tidak berlangganan berdasarkan hasil panggilan layanan web dari doInBackground (). Jadi, jika onPostExecute tidak dipanggil, itu tidak akan muncul ke baris builder.show ().
MSIslam
onPostExecute dipanggil secara default setelah doInBackground, Anda tidak dapat memanggilnya, dan apa pun yang ada di sana akan dieksekusi.
moh.sukhni
1
tugas asinkron Anda akan terus berfungsi setelah pengguna menavigasi dari aktivitas Anda dan ini akan menyebabkan builder.show () meng-crach aplikasi Anda karena tidak ada aktivitas untuk menangani UI untuk Anda. jadi aplikasi Anda menarik data dari web tetapi aktivitas Anda telah dihancurkan sebelum Anda mendapatkan datanya.
moh.sukhni
1

Saya membuat Dialog onCreatedan menggunakannya dengan showdan hide. Bagi saya, akar permasalahannya tidak menampik onBackPressed, yaitu menyelesaikan Homeaktivitas.

@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

Saya menyelesaikan Aktivitas Rumah onBackPressedtanpa menutup / menutup dialog saya.

Ketika saya menutup dialog saya, crash itu menghilang.

new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();
Siddharth
sumber
0

Saya mencoba ini diselesaikan.

 AlertDialog.Builder builder = new AlertDialog.Builder(
                   this);
            builder.setCancelable(true);
            builder.setTitle("Opss!!");

            builder.setMessage("You Don't have anough coins to withdraw. ");
            builder.setMessage("Please read the Withdraw rules.");
            builder.setInverseBackgroundForced(true);
            builder.setPositiveButton("OK",
                    (dialog, which) -> dialog.dismiss());
            builder.create().show();
himanshu kumar
sumber
-1

Coba ini :

    public class <class> extends Activity{

    private AlertDialog.Builder builder;

    public void onCreate(Bundle savedInstanceState) {
                    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
                    super.onCreate(savedInstanceState);

                setContentView(R.layout.<view>); 

                builder = new AlertDialog.Builder(<class>.this);
                builder.setCancelable(true);
                builder.setMessage(<message>);
                builder.setInverseBackgroundForced(true);

        //call the <className> class to execute
}

    private class <className> extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {

    }
    protected void onPostExecute(String result){
        if(page.contains("error")) //when not subscribed
        {   
           if(builder!=null){
                builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton){
                    dialog.dismiss();
                        if(!<condition>)
                        {
                        try
                        {
                        String pl = ""; 

                        mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                        <listener>, pl);
                        }

                        catch(Exception e)
                        {
                        e.printStackTrace();
                        }
                    }  
                }
            });

            builder.show();
        }
    }

}
}
pathe.kiran
sumber
-4

dengan ide variabel global ini, saya menyimpan instance MainActivity di onCreate (); Variabel global Android

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

dan Buka dialog seperti ini. itu berhasil.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Global Var
    globals = (ApplicationController) this.getApplication();
    globals.this_MainActivity = this;
}

dan di utas, saya membuka dialog seperti ini.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Buka MainActivity
  2. Mulai utas.
  3. Buka dialog dari utas -> kerja.
  4. Klik "tombol Kembali" (onCreate akan dipanggil dan hapus MainActivity pertama)
  5. MainActivity baru akan dimulai. (dan simpan instance itu ke global)
  6. Buka dialog dari utas pertama -> dialog akan terbuka dan berfungsi.

:)

Kazuhiko Nakayama
sumber
4
Jangan pernah menyimpan referensi statis ke suatu Aktivitas. Ini akan menyebabkan kebocoran memori
Leandroid