Görünüm, pencere yöneticisi kilitlenmesine bağlı değil


189

Uygulama çökmelerini bildirmek için ACRA kullanıyorum. Bir View not attached to window managerhata mesajı alıyordum ve pDialog.dismiss();bir if deyiminde sararak düzelttiğini düşündüm :

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

View not attached to window managerAldığım çökme miktarını azalttı , ama hala biraz alıyorum ve nasıl çözüleceğinden emin değilim.

Hata mesajı:

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)

Kod snippet'i:

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);
    }
}

Belirgin:

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

Bu çökmenin olmasını engellemek için neyi kaçırıyorum?


1
Bunu hiç çözdün mü? Bendede aynı sorun var. Anlaşılıyor gibi görünmüyor.
tomjung

Ne yazık ki hayır. Biraz ödül kazanabilirim. Size yardımcı olmaları durumunda, bu sorunla ilgilenen diğer bazı konuları kontrol etmelisiniz.
Howli

Senin AsyncTaskiçinde bildirilir Activityveya Fragment?
erakitin

Lütfen "doMoreStuff ()" ve "bir şey ()" işlevlerini yayınlayın.
çılgına

Sorun, ana iş parçacığının çok fazla çalışması nedeniyle olabilir, bu nedenle progressdialog'u göstermek için işleyicileri kullanmaya çalışın ve eğer (pDialog! = Null) bu satıra gerek yoktur, çünkü isShowing'in kendisi iletişim kutusunun devam edip etmediğini kontrol eder.
Madhu

Yanıtlar:


446

Hata nasıl çoğaltılır:

  1. Cihazınızda bu seçeneği etkinleştirin: Settings -> Developer Options -> Don't keep Activities.
  2. AsyncTaskİşlem yapılırken ve gösterilirken Giriş düğmesine basın ProgressDialog.

Android işletim sistemi bir etkinliği gizlendiği anda yok edecektir. Ne zaman onPostExecutedenir Activityolacak "bitirme" devlet ve ProgressDialogbağlı edilmeyecektirActivity .

Nasıl düzeltilir?

  1. Etkinlik durumunuzu kontrol edin. onPostExecuteYönteminizdeki .
  2. Kapat ProgressDialogolarak onDestroyyöntem. Aksi takdirde, android.view.WindowLeakedistisna atılır. Bu istisna genellikle etkinlik sona erdiğinde etkin olan iletişim kutularından gelir.

Bu sabit kodu deneyin:

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);
        }
    }
}

9
Mükemmel cevap! BTW, isShowing () gereksizdir çünkü dismiss (), isShowing () == false ise hiçbir şey yapmaz. Kaynak Kodu
Peter Zhao

3
Kullanmanın avantajı nedir isDestroyed()üzerine isFinishing()bu özel amaç için tüm API'ler üzerinde?
Alexander Abakumov

@AlexanderAbakumov: Aktivitenin sistem tarafından tahrip isFinishing()edilmesi truedurumunda anladığım kadarıyla garanti edilemez , Aktiviteler ile ilgili belgelere bakın .
Markus Penguin

1
@MarkusPenguin: Doğru. Ancak, bu durumda, yazarın yorumundan bir tavsiye kullanmaya kalkışırsa // or call isFinishing() if min sdk version < 17, aynı istisna ile karşılaşır. Bu nedenle, API <17'de çalışan uygulamalar için bu yanıttan farklı bir çözüme ihtiyacımız var.
Alexander Abakumov

@AlexanderAbakumov Ben de aynı sorunu yaşıyorum, ama bitirme benim için çalışmıyor, AsyncCallback içindeki faaliyetime bir WeakReference kaydetme çalıştı ve sonra:myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23

35

Sorun Activity, olmuş finishedya da olmuş olabilir progress of finishing.

Bir kontrol ekleyin isFinishingve yalnızca bu durumdayken iletişim kutusunu kapatınfalse

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

isFinishing: Bu etkinliğin bitirilme sürecinde olup olmadığını kontrol edin, çünkü onu aradınız veya başka biri bitmesini istedi.finish


1
Çek ile istisna hala atıldı
Marcos Vasconcelos

Benim ProgressDialog bir aktivite olmayan bir sınıfta bu yüzden isFinishing check @Libin
Maniraj kullanamıyorum

11

Bir Dialogoluşturulan Fragmentiçin, aşağıdaki kodu kullanın:

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

Bu paterni, Fragmenta'dan ayrı olabileceği durumla başa çıkmak için kullanıyorum Activity.


8

Kuralların burada nasıl çalıştığını görün:

Async görevini çağırdıktan sonra, async görevi arka planda çalışır. bu arzu edilir. Şimdi, bu Async görevi, kodu nasıl göreceğinizi sorarsanız, Etkinliğe bağlı bir ilerleme iletişim kutusuna sahiptir:

pDialog = new ProgressDialog(CLASS.this);

Sen geçiyorsun Class.this argüman bağlam olarak. Bu nedenle İlerleme Durumu iletişim kutusu etkinliğe hala bağlıdır.

Şimdi senaryoyu göz önünde bulundurun: Async görevi devam ederken, finish () yöntemini kullanarak etkinliği bitirmeye çalışırsak, etkinliğe eklenmiş Kaynağa erişmeye çalıştığınız noktadır; progress bar etkinlik artık yokken Orada.

Böylece elde edersiniz:

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

Bunun çözümü:

1) Etkinlik tamamlanmadan önce İletişim kutusunun kapatıldığından veya iptal edildiğinden emin olun.

2) Etkinliği, yalnızca iletişim kutusu kapatıldıktan sonra, yani zaman uyumsuz görev bittikten sonra bitirin.


1
# 2 ile ilgili olarak Activity:; Android her zaman bitirebilir. Yani # 2 mantıklı değil.
Alexander Abakumov

7

@Erakitin yanıtını temel alır, ancak Android API <17 düzeyi için de uyumludur. Ne yazık ki Activity.isDestroyed () yalnızca API düzey 17'den beri desteklenmektedir, bu yüzden benim gibi daha eski bir API düzeyini hedefliyorsanız, kendiniz kontrol edin. Bundan sonra da View not attached to window manageristisna yok.

Örnek kod

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
        }
    }
}

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

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

bakın bu .


3

İlerleme durumu iletişim kutusunu değiştirdikten sonra devre dışı bırakın. İlerleme iletişim kutusu dikey olarak oluşturulursa ve yatay olarak kapatılırsa, Görünüm pencere yöneticisi hatasına bağlı değil görünümünü atar.

Ayrıca ilerleme çubuğunu durdurun ve onPause (), onBackPressed ve onDestroy yöntemindeki zaman uyumsuz görevi durdurun.

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

    asyncTaskObj.cancel(true);

}

3

Etkinliği Yok Et ve İletişim Kutunuzu Kapat & null Yap

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

3

İlk olarak, kilitlenme nedeni decorView'in dizini -1'dir, Android kaynak kodundan biliyor olabiliriz, kod snippet'i vardır:

sınıfı: android.view.WindowManagerGlobal

Dosya: 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;
    }

Bu nedenle, çözünürlüğü takip edelim, sadece decorView'in dizinini yargılayın, eğer 0'dan fazla ise devam edin veya geri dönün ve vazgeç, vazgeç, kod aşağıdaki gibi:

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();
        }

2

Aşağıdaki dismiss()gibi yöntemi geçersiz kılın :

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

Sorunu yeniden oluşturmak için iletişim kutusunu kapatmadan önce etkinliği bitirmeniz yeterlidir.


1

en iyi çözüm. Etkinlik bağlam sonra sadece etkinlik bitmiş olduğunu kontrol etmek veya daha sonra çağrı değilse ilk bağlam kontrol faaliyeti bağlam veya uygulama bağlamını olduğu dialog.show()veyadialog.dismiss();

Aşağıdaki örnek koda bakın ... faydalı olacağını umuyoruz!

Ekran iletişim kutusu

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

İletişim kutusunu kapat

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

Daha fazla kontrol eklemek istiyorsanız, koşul ekleyin dialog.isShowing()veya dialog !-nullkullanın &&.


0

Bu sorun nedeniyle reddetme işlevi çağrılmadan önce etkinliğiniz biter. İstisna ile başa çıkın ve ADB günlüğünüzü tam olarak kontrol edin.

/**
     * 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);
}

0

Bu istisnayı yeniden üretmenin bir yolu var.

2 kullanıyorum AsyncTask. Biri uzun ve diğeri kısa görev yapar. Kısa görev tamamlandıktan sonra arayın finish(). Görev tamamlandığında ve Dialog.dismiss()çağrıldığında çöküyor.

İşte benim örnek kod:

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");
    }
}

Bunu deneyebilir ve bu sorunu çözmenin en iyi yolunun ne olduğunu öğrenebilirsiniz. Çalışmamdan, düzeltmenin en az 4 yolu var:

  1. @ erakitin'in cevabı: isFinishing()etkinliğin durumunu kontrol etme çağrısı
  2. @ Kapé'nin cevabı: etkinliğin durumunu kontrol etmek için bayrak ayarlayın
  3. Ele almak için dene / yakala seçeneğini kullanın.
  4. Çağrı AsyncTask.cancel(false)içinde onDestroy(). Zaman uyumsuzluğun yürütülmesini engeller, bunun yerine onPostExecute()yürütür onCancelled().
    Not: Android 2.XX gibi eski Android işletim sistemlerinde onPostExecute()bile aramanıza rağmenAsyncTask.cancel(false)

Sizin için en iyisini seçebilirsiniz.


0

PDialog'u global olarak başlatmanız, sonra kaldırmanız ve yerel görünümünüzü veya iletişiminizi başlatmanız olabilir.Aynı sorunum var, bunu yaptım ve sorunum çözüldü. Umarım sizin için çalışır.


0

onPauseyöntem veya onDestroyyöntem konusundaki iletişim kutumuzu da kapattık

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

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.