Tampilan android tidak terpasang ke pengelola jendela

111

Saya mengalami beberapa pengecualian berikut:

java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:191)
at android.view.Window$LocalWindowManager.updateViewLayout(Window.java:428)
at android.app.Dialog.onWindowAttributesChanged(Dialog.java:596)
at android.view.Window.setDefaultWindowFormat(Window.java:1013)
at com.android.internal.policy.impl.PhoneWindow.access$700(PhoneWindow.java:86)
at com.android.internal.policy.impl.PhoneWindow$DecorView.drawableChanged(PhoneWindow.java:1951)
at com.android.internal.policy.impl.PhoneWindow$DecorView.fitSystemWindows(PhoneWindow.java:1889)
at android.view.ViewRoot.performTraversals(ViewRoot.java:727)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1633)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4338)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)

Saya telah mencari di Google dan melihat bahwa itu ada hubungannya dengan munculan dan memutar layar, tetapi tidak ada referensi ke kode saya.

Pertanyaannya adalah:

  1. apakah ada cara untuk mengetahui dengan tepat kapan masalah ini terjadi?
  2. selain memutar layar, apakah ada peristiwa atau tindakan lain yang memicu error ini?
  3. bagaimana cara mencegah hal ini terjadi?
Daniel Benedykt
sumber
1
Lihat apakah Anda bisa menjelaskan bagaimana aktivitas dijelaskan dalam manifes dan aktivitas apa yang ada di layar saat kesalahan terjadi. Lihat apakah Anda dapat memecahkan masalah Anda menjadi kasus percobaan minimal.
Josh Lee
Mungkin Anda mencoba mengubah tampilan Anda sebelum View#onAttachedToWindow()dipanggil?
Alex Lockwood

Jawaban:

163

Saya mengalami masalah ini di mana pada orientasi layar berubah, aktivitas selesai sebelum AsyncTask dengan dialog kemajuan selesai. Sepertinya saya menyelesaikan ini dengan menyetel dialog ke null onPause()dan kemudian memeriksanya di AsyncTask sebelum menutupnya.

@Override
public void onPause() {
    super.onPause();

    if ((mDialog != null) && mDialog.isShowing())
        mDialog.dismiss();
    mDialog = null;
}

... di AsyncTask saya:

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...",
            true);
}

protected void onPostExecute(Object result) {
   if ((mDialog != null) && mDialog.isShowing()) { 
        mDialog.dismiss();
   }
}
johnnyblizzard
sumber
12
@YekhezkelYovel, AsyncTask sedang berjalan di utas yang bertahan dari aktivitas restart dan mungkin menyimpan referensi ke Activity yang sekarang sudah mati dan Views-nya (ini adalah kebocoran memori yang efektif, meskipun mungkin jangka pendek - tergantung berapa lama tugas yang dibutuhkan untuk menyelesaikannya ). Solusi di atas berfungsi dengan baik, jika Anda senang menerima kebocoran memori jangka pendek. Pendekatan yang disarankan adalah membatalkan () semua AsyncTask yang sedang berjalan saat Aktivitas dijeda.
Stevie
8

Setelah berkelahi dengan masalah ini, saya akhirnya mendapatkan solusi ini:

/**
 * Dismiss {@link ProgressDialog} with check for nullability and SDK version
 *
 * @param dialog instance of {@link ProgressDialog} to dismiss
 */
public void dismissProgressDialog(ProgressDialog dialog) {
    if (dialog != null && dialog.isShowing()) {

            //get the Context object that was used to great the dialog
            Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();

            // if the Context used here was an activity AND it hasn't been finished or destroyed
            // then dismiss it
            if (context instanceof Activity) {

                // Api >=17
                if (!((Activity) context).isFinishing() {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        if (!((Activity) context).isDestroyed()) {
                            dismissWithExceptionHandling(dialog);
                        }
                    } else { 
                        // Api < 17. Unfortunately cannot check for isDestroyed()
                        dismissWithExceptionHandling(dialog);
                    }
                }
            } else
                // if the Context used wasn't an Activity, then dismiss it too
                dismissWithExceptionHandling(dialog);
        }
        dialog = null;
    }
}

/**
 * Dismiss {@link ProgressDialog} with try catch
 *
 * @param dialog instance of {@link ProgressDialog} to dismiss
 */
public void dismissWithExceptionHandling(ProgressDialog dialog) {
    try {
        dialog.dismiss();
    } catch (final IllegalArgumentException e) {
        // Do nothing.
    } catch (final Exception e) {
        // Do nothing.
    } finally {
        dialog = null;
    }
}

Terkadang, penanganan pengecualian yang baik berfungsi dengan baik jika tidak ada solusi yang lebih baik untuk masalah ini.

blueware
sumber
5

Jika Anda memiliki Activitybenda yang berkeliaran, Anda dapat menggunakan isDestroyed()metode ini:

Activity activity;

// ...

if (!activity.isDestroyed()) {
    // ...
}

Ini bagus jika Anda memiliki AsyncTasksubkelas non-anonim yang Anda gunakan di berbagai tempat.

shawkinaw.dll
sumber
3

Saya menggunakan kelas statis kustom yang membuat- menunjukkan dan menyembunyikan dialog. kelas ini digunakan oleh kegiatan lain juga tidak hanya satu kegiatan. Sekarang masalah yang Anda jelaskan juga muncul pada saya dan saya telah menginap untuk mencari solusinya ..

Akhirnya saya sampaikan solusinya!

jika Anda ingin menampilkan atau menutup dialog dan Anda tidak tahu aktivitas mana yang memulai dialog untuk menyentuhnya, maka kode berikut ini untuk Anda ..

 static class CustomDialog{

     public static void initDialog(){
         ...
         //init code
         ...
     }

      public static void showDialog(){
         ...
         //init code for show dialog
         ...
     }

     /****This is your Dismiss dialog code :D*******/
     public static void dismissProgressDialog(Context context) {                
            //Can't touch other View of other Activiy..
            //http://stackoverflow.com/questions/23458162/dismiss-progress-dialog-in-another-activity-android
            if ( (progressdialog != null) && progressdialog.isShowing()) {

                //is it the same context from the caller ?
                Log.w("ProgressDIalog dismiss", "the dialog is from"+progressdialog.getContext());

                Class caller_context= context.getClass();
                Activity call_Act = (Activity)context;
                Class progress_context= progressdialog.getContext().getClass();

                Boolean is_act= ( (progressdialog.getContext()) instanceof  Activity )?true:false;
                Boolean is_ctw= ( (progressdialog.getContext()) instanceof  ContextThemeWrapper )?true:false;

                if (is_ctw) {
                    ContextThemeWrapper cthw=(ContextThemeWrapper) progressdialog.getContext();
                    Boolean is_same_acivity_with_Caller= ((Activity)(cthw).getBaseContext() ==  call_Act )?true:false;

                    if (is_same_acivity_with_Caller){
                        progressdialog.dismiss();
                        progressdialog = null;
                    }
                    else {
                        Log.e("ProgressDIalog dismiss", "the dialog is NOT from the same context! Can't touch.."+((Activity)(cthw).getBaseContext()).getClass());
                        progressdialog = null;
                    }
                }


            }
        } 

 }
aimiliano
sumber
1

Solusi di atas tidak berhasil untuk saya. Jadi yang saya lakukan adalah mengambil ProgressDialogsecara global dan kemudian menambahkan ini ke aktivitas saya

@Override
    protected void onDestroy() {
        if (progressDialog != null && progressDialog.isShowing())
            progressDialog.dismiss();
        super.onDestroy();
    }

Sehingga jika aktivitas dimusnahkan maka ProgressDialog juga akan dimusnahkan.

Kishan Solanki
sumber
0

Untuk pertanyaan 1):

Mengingat pesan kesalahan tampaknya tidak mengatakan baris mana dari kode Anda yang menyebabkan masalah, Anda dapat melacaknya dengan menggunakan breakpoint. Breakpoints menghentikan eksekusi program ketika program mencapai baris kode tertentu. Dengan menambahkan breakpoint ke lokasi penting, Anda dapat menentukan baris kode mana yang menyebabkan error. Misalnya, jika program Anda mengalami error pada baris setContentView (), Anda bisa meletakkan breakpoint di sana. Saat program berjalan, program akan berhenti sebelum menjalankan jalur tersebut. Jika kemudian melanjutkan menyebabkan program macet sebelum mencapai breakpoint berikutnya, Anda kemudian tahu bahwa baris yang mematikan program berada di antara dua breakpoint.

Menambahkan titik putus itu mudah jika Anda menggunakan Eclipse. Klik kanan di margin tepat di sebelah kiri kode Anda dan pilih "Toggle breakpoint". Anda kemudian perlu menjalankan aplikasi Anda dalam mode debug, tombol yang terlihat seperti serangga hijau di sebelah tombol jalankan normal. Ketika program mencapai breakpoint, Eclipse akan beralih ke perspektif debug dan menunjukkan baris yang ditunggu. Untuk memulai program berjalan lagi, cari tombol 'Lanjutkan', yang terlihat seperti 'Putar' normal tetapi dengan bilah vertikal di sebelah kiri segitiga.

Anda juga dapat mengisi aplikasi Anda dengan Log.d ("Aplikasi saya", "Beberapa informasi di sini yang memberi tahu Anda di mana baris log"), yang kemudian memposting pesan di jendela LogCat Eclipse. Jika Anda tidak dapat menemukan jendela itu, buka dengan Window -> Show View -> Other ... -> Android -> LogCat.

Semoga membantu!

Steve Haley
sumber
7
Masalah ini terjadi pada ponsel klien, jadi saya tidak punya opsi untuk men-debug. Juga masalah ini terjadi sepanjang waktu, hanya terjadi kadang-kadang, jadi tidak tahu cara melacaknya
Daniel Benedykt
Anda masih bisa mendapatkan pesan debug dari telepon, jika Anda bisa mendapatkannya. Di menu -> pengaturan -> aplikasi -> pengembangan, ada opsi untuk USB Debugging. Jika diaktifkan, Anda kemudian dapat mencolokkan telepon dan LogCat menangkap semua jalur debugging normal. Selain itu ... yah ... intermittency dapat dijelaskan olehnya tergantung pada status program. Saya kira Anda harus bermain-main menggunakan program untuk melihat apakah Anda dapat menciptakan kembali masalah tersebut.
Steve Haley
4
Seperti yang saya katakan sebelumnya, masalah terjadi pada telepon klien, dan saya tidak memiliki akses ke sana. Klien mungkin berada di benua lain :)
Daniel Benedykt
Ada aplikasi di pasaran yang akan mengirimi Anda logcat mereka melalui email.
Tim Green
1
Sekadar info, Anda dapat memutar emulator dengan menekan tombol numpad 9
Rob
0

Saya menambahkan berikut ini ke manifes untuk aktivitas itu

android:configChanges="keyboardHidden|orientation|screenLayout"
Rickey
sumber
19
Ini bukan solusi: Sistem dapat menghancurkan aktivitas Anda karena alasan lain juga.
Roel
1
Bisakah Anda menjelaskan jawaban Anda?
Leebeedev
0

sesuai dengan kode windowManager (tautan di sini ), ini terjadi saat tampilan yang Anda coba perbarui (yang mungkin termasuk dalam dialog, tetapi tidak perlu) tidak lagi terpasang ke root windows yang sebenarnya.

seperti yang disarankan orang lain, Anda harus memeriksa status aktivitas sebelum melakukan operasi khusus pada dialog Anda.

inilah kode yang relevan, yang merupakan penyebab masalah (disalin dari kode sumber Android):

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams
            = (WindowManager.LayoutParams)params;

    view.setLayoutParams(wparams);

    synchronized (this) {
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots[index];
        mParams[index] = wparams;
        root.setLayoutParams(wparams, false);
    }
}

private int findViewLocked(View view, boolean required) {
        synchronized (this) {
            final int count = mViews != null ? mViews.length : 0;
            for (int i=0; i<count; i++) {
                if (mViews[i] == view) {
                    return i;
                }
            }
            if (required) {
                throw new IllegalArgumentException(
                        "View not attached to window manager");
            }
            return -1;
        }
    }
pengembang android
sumber
kode yang saya tulis di sini adalah apa yang dilakukan google. apa yang saya tulis adalah untuk menunjukkan penyebab masalah ini.
Pengembang android
0

Masalah saya diselesaikan dengan uhmengunci rotasi layar di android saya, aplikasi yang menyebabkan saya masalah sekarang bekerja dengan sempurna

Roger
sumber
0

Opsi lainnya adalah tidak memulai tugas asinkron hingga dialog dilampirkan ke jendela dengan menimpa onAttachedToWindow () pada dialog, dengan cara itu ia selalu dapat ditutup.

Nick Palmer
sumber
0

Atau Cukup Anda Dapat menambahkan

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...", true, false);
}

yang akan membuat ProgressDialogto tidak bisa dibatalkan

Sarith Vasu
sumber
-2

Mengapa tidak mencoba menangkap, seperti ini:

protected void onPostExecute(Object result) {
        try {
            if ((mDialog != null) && mDialog.isShowing()) {
                mDialog.dismiss();
            }
        } catch (Exception ex) {
            Log.e(TAG, ex.getMessage(), ex);
        }
    }
Heasen
sumber
IllegalStateOfException Harus ditangani dengan cara yang lebih baik, bukan hanya berperilaku tidak normal.
Lavakush
-3

saat Anda mendeklarasikan aktivitas dalam manifes, Anda memerlukan android: configChanges = "orientasi"

contoh:

<activity android:theme="@android:style/Theme.Light.NoTitleBar" android:configChanges="orientation"  android:label="traducción" android:name=".PantallaTraductorAppActivity"></activity>
Cristian
sumber
1
Tag ini diperlukan hanya jika pengembang tidak ingin menghancurkan dan membuat ulang aktivitas oleh sistem android.
Ankit