Lihat tidak terlampir pada crash manajer jendela

189

Saya menggunakan ACRA untuk melaporkan kerusakan aplikasi. Saya menerima View not attached to window managerpesan kesalahan dan berpikir saya telah memperbaikinya dengan membungkus pDialog.dismiss();dalam pernyataan if:

if (pDialog!=null) 
{
    if (pDialog.isShowing()) 
    {
        pDialog.dismiss();   
    }
}

Ini telah mengurangi jumlah View not attached to window managercrash yang saya terima, tetapi saya masih mendapatkan beberapa crash dan saya tidak yakin bagaimana menyelesaikannya.

Pesan eror:

java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:425)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:327)
at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:83)
at android.app.Dialog.dismissDialog(Dialog.java:330)
at android.app.Dialog.dismiss(Dialog.java:312)
at com.package.class$LoadAllProducts.onPostExecute(class.java:624)
at com.package.class$LoadAllProducts.onPostExecute(class.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5419)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
at dalvik.system.NativeStart.main(Native Method)

Cuplikan kode:

class LoadAllProducts extends AsyncTask<String, String, String> 
{

    /**
     * Before starting background thread Show Progress Dialog
     * */
    @Override
    protected void onPreExecute() 
    {
        super.onPreExecute();
        pDialog = new ProgressDialog(CLASS.this);
        pDialog.setMessage("Loading. Please wait...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(false);
        pDialog.show();
    }

    /**
     * getting All products from url
     * */
    protected String doInBackground(String... args) 
    {
        // Building Parameters
        doMoreStuff("internet");
        return null;
    }


    /**
     * After completing background task Dismiss the progress dialog
     * **/
    protected void onPostExecute(String file_url) 
    {
         // dismiss the dialog after getting all products
         if (pDialog!=null) 
         {
                if (pDialog.isShowing()) 
                {
                    pDialog.dismiss();   //This is line 624!    
                }
         }
         something(note);
    }
}

Nyata:

    <activity
        android:name="pagename.CLASS" 
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout"            
        android:label="@string/name" >
    </activity>

Apa yang saya lewatkan untuk menghentikan kecelakaan ini terjadi?

Howli
sumber
1
Apakah Anda pernah memikirkan ini? Saya mempunyai masalah yang sama. Sepertinya tidak bisa memahaminya.
tomjung
Sayangnya tidak ada. Saya mungkin memulai hadiah dalam sedikit. Anda harus memeriksa beberapa utas lainnya yang berurusan dengan masalah jika mereka membantu Anda.
Howli
Anda AsyncTaskdinyatakan di dalam Activityatau Fragment?
erakitin
Silakan memposting fungsi "doMoreStuff ()" dan "something ()".
mengamuk
Masalahnya mungkin karena terlalu banyak pekerjaan utas utama, jadi coba gunakan penangan untuk menunjukkan progressdialog, dan jika (pDialog! = Null) baris ini tidak perlu, karena isShowing sendiri memeriksa apakah dialog sedang berlangsung atau tidak.
Madhu

Jawaban:

446

Cara mereproduksi bug:

  1. Mengaktifkan opsi ini pada perangkat Anda: Settings -> Developer Options -> Don't keep Activities.
  2. Tekan tombol Rumah saat AsyncTasksedang mengeksekusi dan ProgressDialogsedang ditampilkan.

OS Android akan menghancurkan aktivitas segera setelah disembunyikan. Kapan onPostExecutedisebut Activityakan dalam "finishing" dan ProgressDialogtidak akan dilampirkan Activity.

Bagaimana memperbaikinya:

  1. Periksa status aktivitas dalam onPostExecutemetode Anda .
  2. Singkirkan metode ProgressDialogin onDestroy. Kalau tidak, android.view.WindowLeakedpengecualian akan dibuang. Pengecualian ini biasanya berasal dari dialog yang masih aktif ketika aktivitas selesai.

Coba kode tetap ini:

public class YourActivity extends Activity {

    private void showProgressDialog() {
        if (pDialog == null) {
            pDialog = new ProgressDialog(StartActivity.this);
            pDialog.setMessage("Loading. Please wait...");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(false);
        }
        pDialog.show();
    }

    private void dismissProgressDialog() {
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }

    @Override
    protected void onDestroy() {
        dismissProgressDialog();
        super.onDestroy();
    }

    class LoadAllProducts extends AsyncTask<String, String, String> {

        // Before starting background thread Show Progress Dialog
        @Override
        protected void onPreExecute() {
            showProgressDialog();
        }

        //getting All products from url
        protected String doInBackground(String... args) {
            doMoreStuff("internet");
            return null;
        }

        // After completing background task Dismiss the progress dialog
        protected void onPostExecute(String file_url) {
            if (YourActivity.this.isDestroyed()) { // or call isFinishing() if min sdk version < 17
                return;
            }
            dismissProgressDialog();
            something(note);
        }
    }
}
erakitin
sumber
9
Jawaban bagus! BTW, isShowing () tidak perlu karena memberhentikan () tidak akan melakukan apa-apa jika isShowing () == false. Source Code
Peter Zhao
3
Apa manfaat menggunakan isDestroyed()lebih isFinishing()dari semua API untuk tujuan khusus ini?
Alexander Abakumov
@AlexanderAbakumov: Dari apa yang saya pahami isFinishing()tidak dijamin truejika aktivitas dihancurkan oleh sistem, lihat dokumentasi tentang Aktivitas .
Markus Penguin
1
@MarkusPenguin: Benar. Tetapi, dalam kasus ini, jika seseorang mencoba menggunakan saran dari komentar penulis // or call isFinishing() if min sdk version < 17, ia mengalami pengecualian yang sama. Jadi, kami membutuhkan solusi yang berbeda dari jawaban ini untuk aplikasi yang berjalan di API <17.
Alexander Abakumov
@AlexanderAbakumov Saya mengalami masalah yang sama, tetapi apakah Menyelesaikan tidak bekerja untuk saya, Berhasil menyimpan Referensi Lemah untuk aktivitas saya di dalam AsyncCallback, dan kemudian: myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23
35

Masalahnya bisa jadi Activitysudah finishedatau sudah masukprogress of finishing .

Tambahkan tanda centang isFinishing, dan abaikan dialog hanya saat inifalse

if (!YourActivity.this.isFinishing() && pDialog != null) {
    pDialog.dismiss();
}

isFinishing: Periksa untuk melihat apakah kegiatan ini sedang dalam proses penyelesaian, baik karena Anda memanggilnya finishatau orang lain telah meminta agar kegiatannya selesai.

Libin
sumber
1
Dengan cek pengecualian masih dilemparkan
Marcos Vasconcelos
ProgressDialog saya ada di kelas yang bukan merupakan kegiatan jadi saya tidak dapat menggunakan cek isFinishing @Libin
Maniraj
11

Untuk Dialogdibuat dalam a Fragment, saya menggunakan kode berikut:

ProgressDialog myDialog = new ProgressDialog(getActivity());
myDialog.setOwnerActivity(getActivity());
...
Activity activity = myDialog.getOwnerActivity();
if( activity!=null && !activity.isFinishing()) {
    myDialog.dismiss();
}

Saya menggunakan pola ini untuk menangani kasus ketika Fragmentmungkin terlepas dari Activity.

Teng-pao Yu
sumber
8

Lihat bagaimana Kode bekerja di sini:

Setelah memanggil tugas Async, tugas async berjalan di latar belakang. itu yang diinginkan. Sekarang, tugas Async ini memiliki dialog progres yang dilampirkan ke Activity, jika Anda bertanya bagaimana melihat kode:

pDialog = new ProgressDialog(CLASS.this);

Anda melewati Class.this konteks as ke argumen. Jadi dialog Progress masih melekat pada aktivitas.

Sekarang pertimbangkan skenario: Jika kami mencoba menyelesaikan aktivitas menggunakan metode finish (), saat tugas async sedang berlangsung, adalah titik di mana Anda mencoba mengakses Sumberdaya yang terlampir pada aktivitas yaitu progress bar saat aktivitas tidak lagi sana.

Karenanya Anda mendapatkan:

java.lang.IllegalArgumentException: View not attached to the window manager

Solusi untuk ini:

1) Pastikan kotak Dialog dibubarkan atau dibatalkan sebelum aktivitas selesai.

2) Selesaikan aktivitas, hanya setelah kotak dialog diberhentikan, tugas async selesai.

Kailas
sumber
1
Mengenai # 2: Anda tidak memiliki kontrol penuh kapan harus menyelesaikan Activity; Android dapat menyelesaikannya kapan saja. Jadi, # 2 tidak masuk akal.
Alexander Abakumov
7

Berdasarkan jawaban @erakitin, tetapi juga kompatibel untuk versi Android <API level 17. Sedihnya Activity.isDestroyed () hanya didukung sejak API level 17, jadi jika Anda menargetkan level API yang lebih lama seperti saya, Anda harus periksa sendiri. Belum mendapatkan View not attached to window managerpengecualian setelah itu.

Kode contoh

public class MainActivity extends Activity {
    private TestAsyncTask mAsyncTask;
    private ProgressDialog mProgressDialog;
    private boolean mIsDestroyed;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (condition) {
            mAsyncTask = new TestAsyncTask();
            mAsyncTask.execute();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mAsyncTask != null && mAsyncTask.getStatus() != AsyncTask.Status.FINISHED) {
            Toast.makeText(this, "Still loading", Toast.LENGTH_LONG).show();
            return;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIsDestroyed = true;

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

    public class TestAsyncTask extends AsyncTask<Void, Void, AsyncResult> {    
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgressDialog = ProgressDialog.show(MainActivity.this, "Please wait", "doing stuff..");
        }

        @Override
        protected AsyncResult doInBackground(Void... arg0) {
            // Do long running background stuff
            return null;
        }

        @Override
        protected void onPostExecute(AsyncResult result) {
            // Use MainActivity.this.isDestroyed() when targeting API level 17 or higher
            if (mIsDestroyed)// Activity not there anymore
                return;

            mProgressDialog.dismiss();
            // Handle rest onPostExecute
        }
    }
}
Kapé
sumber
4
@Override
public void onPause() {
    super.onPause();

    if(pDialog != null)
        pDialog .dismiss();
    pDialog = null;
}

lihat ini .

Meghna
sumber
3

Override onConfigurationChanged dan abaikan dialog progres. Jika dialog progres dibuat dalam potret dan ditolak dalam lanskap, maka dialog itu akan menampilkan Lihat tidak terlampir pada kesalahan manajer jendela.

Juga hentikan bilah progres dan hentikan tugas asinkron dalam metode onPause (), onBackPressed dan onDestroy.

if(asyncTaskObj !=null && asyncTaskObj.getStatus().equals(AsyncTask.Status.RUNNING)){

    asyncTaskObj.cancel(true);

}
Pratapi Hemant Patel
sumber
3

Ganti onDestroy dari Aktivitas dan Singkirkan Dialog Anda & jadikan null

protected void onDestroy ()
    {
        if(mProgressDialog != null)
            if(mProgressDialog.isShowing())
                mProgressDialog.dismiss();
        mProgressDialog= null;
    }
surya
sumber
3

Pertama, alasan crash adalah indeks decorView -1, kita bisa mengetahuinya dari kode sumber Android, ada potongan kode:

kelas: android.view.WindowManagerGlobal

file: WindowManagerGlobal.java

private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
//here, view is decorView,comment by OF
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

jadi kita mendapatkan resolusi ikuti, hanya menilai indeks decorView, jika lebih dari 0 lalu melanjutkan atau hanya kembali dan berhenti, kode sebagai berikut:

try {
            Class<?> windowMgrGloable = Class.forName("android.view.WindowManagerGlobal");
            try {
                Method mtdGetIntance = windowMgrGloable.getDeclaredMethod("getInstance");
                mtdGetIntance.setAccessible(true);
                try {
                    Object windownGlobal = mtdGetIntance.invoke(null,null);
                    try {
                        Field mViewField = windowMgrGloable.getDeclaredField("mViews");
                        mViewField.setAccessible(true);
                        ArrayList<View> mViews = (ArrayList<View>) mViewField.get(windownGlobal);
                        int decorViewIndex = mViews.indexOf(pd.getWindow().getDecorView());
                        Log.i(TAG,"check index:"+decorViewIndex);
                        if (decorViewIndex < 0) {
                            return;
                        }
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (pd.isShowing()) {
            pd.dismiss();
        }
aolphn
sumber
2

Overide dismiss()metode seperti ini:

@Override
public void dismiss() {
    Window window = getWindow();
    if (window == null) {
        return;
    }
    View decor = window.getDecorView();
    if (decor != null && decor.getParent() != null) {
        super.dismiss();
    }
}

Untuk mereproduksi masalah, selesaikan aktivitas sebelum mengabaikan dialog.

petir
sumber
1

solusi terbaik. Periksa konteks pertama adalah konteks aktivitas atau konteks aplikasi jika konteks aktivitas maka hanya memeriksa aktivitas selesai atau tidak maka panggilan dialog.show()ataudialog.dismiss();

Lihat contoh kode di bawah ini ... semoga bermanfaat!

Dialog tampilan

if (context instanceof Activity) {
   if (!((Activity) context).isFinishing())
     dialog.show();
}

Singkirkan dialog

if (context instanceof Activity) {
       if (!((Activity) context).isFinishing())
         dialog.dismiss();
    }

Jika Anda ingin menambahkan lebih banyak cek maka tambahkan dialog.isShowing()atau dialog !-nullgunakan &&ketentuan.

Yogesh Rathi
sumber
0

Masalah ini karena aktivitas Anda selesai sebelum fungsi pemberhentian dipanggil. Tangani pengecualian dan periksa log ADB Anda untuk alasan yang tepat.

/**
     * After completing background task Dismiss the progress dialog
     * **/
    protected void onPostExecute(String file_url) {
    try {
         if (pDialog!=null) {
            pDialog.dismiss();   //This is line 624!    
         }
    } catch (Exception e) {
        // do nothing
    }
     something(note);
}
Rajiv Manivannan
sumber
0

Saya punya cara untuk mereproduksi pengecualian ini.

Saya menggunakan 2 AsyncTask. Satu melakukan tugas panjang dan yang lainnya melakukan tugas pendek. Setelah tugas singkat selesai, hubungi finish(). Ketika tugas yang panjang selesai dan panggilan Dialog.dismiss(), itu crash.

Ini kode contoh saya:

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");

        new AsyncTask<Void, Void, Void>(){
            @Override
            protected void onPreExecute() {
                mProgressDialog = ProgressDialog.show(MainActivity.this, "", "plz wait...", true);
            }

            @Override
            protected Void doInBackground(Void... nothing) {
                try {
                    Log.d(TAG, "long thread doInBackground");
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                Log.d(TAG, "long thread onPostExecute");
                if (mProgressDialog != null && mProgressDialog.isShowing()) {
                    mProgressDialog.dismiss();
                    mProgressDialog = null;
                }
                Log.d(TAG, "long thread onPostExecute call dismiss");
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

        new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Log.d(TAG, "short thread doInBackground");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                Log.d(TAG, "short thread onPostExecute");
                finish();
                Log.d(TAG, "short thread onPostExecute call finish");
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

Anda dapat mencoba ini dan mencari tahu apa cara terbaik untuk memperbaiki masalah ini. Dari penelitian saya, setidaknya ada 4 cara untuk memperbaikinya:

  1. @ jawaban erakitin: panggil isFinishing()untuk memeriksa status aktivitas
  2. @ Kapé's answer: set flag untuk memeriksa keadaan aktivitas
  3. Gunakan try / catch untuk menanganinya.
  4. Sebut AsyncTask.cancel(false)di onDestroy(). Ini akan mencegah asynctask untuk mengeksekusi onPostExecute()tetapi mengeksekusi onCancelled()sebagai gantinya.
    Catatan: onPostExecute()masih akan mengeksekusi bahkan jika Anda memanggil AsyncTask.cancel(false)OS Android yang lebih lama, seperti Android 2.XX

Anda dapat memilih yang terbaik untuk Anda.

Yueh-Ming Chien
sumber
0

Mungkin Anda menginisialisasi pDialog secara global, kemudian hapus dan intialize tampilan atau dialog Anda secara lokal. Saya memiliki masalah yang sama, saya telah melakukan ini dan masalah saya sudah teratasi. Semoga ini akan berhasil untuk Anda.

Shashwat Gupta
sumber
0

kami juga telah mengabaikan dialog kami tentang onPausemetode atau onDestroymetode

@Override
protected void onPause() {
    super.onPause();
    dialog.dismiss();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
tarun bhola
sumber