Ansicht nicht an Windows Manager-Absturz angehängt


188

Ich verwende ACRA, um App-Abstürze zu melden. Ich erhielt eine View not attached to window managerFehlermeldung und dachte, ich hätte sie behoben, indem ich die pDialog.dismiss();if-Anweisung umschloss:

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

Es hat die Anzahl der View not attached to window managerAbstürze reduziert, die ich erhalte, aber ich bekomme immer noch einige und bin mir nicht sicher, wie ich es lösen soll.

Fehlermeldung:

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)

Code-Auszug:

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

Manifest:

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

Was fehlt mir, um diesen Absturz zu verhindern?


1
Hast du das jemals herausgefunden? Ich habe das gleiche Problem. Ich kann es nicht herausfinden.
Tomjung

Unglücklicherweise nicht. Ich könnte gleich ein Kopfgeld anfangen. Sie sollten einige der anderen Threads überprüfen, die sich mit diesem Problem befassen, falls sie Ihnen helfen.
Howli

Dein AsyncTaskist drinnen deklariert Activityoder Fragment?
Erakitin

Bitte posten Sie die Funktionen "doMoreStuff ()" und "etwas ()".
Berserker

Das Problem kann auf zu viel Arbeit des Hauptthreads zurückzuführen sein. Versuchen Sie daher, Handler zu verwenden, um den Fortschrittsdialog anzuzeigen. Wenn (pDialog! = Null) diese Zeile nicht erforderlich ist, überprüfen Sie anhand von isShowing selbst, ob der Dialog gerade ausgeführt wird oder nicht.
Madhu

Antworten:


446

So reproduzieren Sie den Fehler:

  1. Aktivieren Sie diese Option auf Ihrem Gerät : Settings -> Developer Options -> Don't keep Activities.
  2. Drücken Sie die Home-Taste, während das AsyncTaskausgeführt wird und das ProgressDialogangezeigt wird.

Das Android-Betriebssystem zerstört eine Aktivität, sobald sie ausgeblendet ist. Wenn onPostExecuteaufgerufen Activitywird, befindet sich der Wille im Zustand "Fertig stellen" und der ProgressDialogWille wird nicht angehängt Activity.

Wie man es repariert:

  1. Überprüfen Sie den Aktivitätsstatus Ihrer onPostExecuteMethode.
  2. Verwerfen Sie die ProgressDialogin- onDestroyMethode. Andernfalls wird eine android.view.WindowLeakedAusnahme ausgelöst. Diese Ausnahme tritt normalerweise in Dialogen auf, die nach Abschluss der Aktivität noch aktiv sind.

Versuchen Sie diesen festen Code:

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

8
Gute Antwort! Übrigens ist isShowing () nicht erforderlich, da Entlassung () nichts bewirkt, wenn isShowing () == false ist. Quellcode
Peter Zhao

3
Was ist der Vorteil der Verwendung von isDestroyed()mehr als isFinishing()für diesen Zweck auf alle APIs?
Alexander Abakumov

@AlexanderAbakumov: Soweit ich weiß, kann isFinishing()nicht garantiert werden, truedass die Aktivität vom System zerstört wird. Weitere Informationen finden Sie in der Dokumentation zu Aktivitäten .
Markus Penguin

1
@ MarkusPinguin: Richtig. Wenn man jedoch in diesem Fall versucht, einen Rat aus dem Kommentar des Autors zu verwenden, stößt // or call isFinishing() if min sdk version < 17er auf dieselbe Ausnahme. Wir brauchen also eine andere Lösung als diese Antwort für Apps, die auf API <17 ausgeführt werden.
Alexander Abakumov

@ AlexanderAbakumov Ich habe das gleiche Problem, aber das Finishing funktioniert nicht für mich. Es hat funktioniert, einen schwachen Bezug zu meiner Aktivität im AsyncCallback zu speichern und dann:myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23

34

Problem könnte sein, dass die gewesen Activitysind finishedoder in progress of finishing.

Fügen Sie einen Scheck hinzu isFinishingund schließen Sie den Dialog nur dann, wenn dies der Fall istfalse

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

isFinishing: Überprüfen Sie, ob diese Aktivität gerade beendet wird, entweder weil Sie sie aufgerufen finishhaben oder weil jemand anderes sie beendet hat.


1
Mit dem Scheck wird die Ausnahme noch geworfen
Marcos Vasconcelos

Mein ProgressDialog befindet sich in einer Klasse, die keine Aktivität ist, daher kann ich den isFinishing-Check @Libin
Maniraj am

10

Für die DialogErstellung in a verwende Fragmentich den folgenden Code:

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

Ich benutze dieses Muster, um den Fall zu behandeln, in dem a Fragmentvon der gelöst werden kann Activity.


8

Sehen Sie hier, wie der Code funktioniert:

Nach dem Aufrufen der Async-Task wird die Async-Task im Hintergrund ausgeführt. das ist wünschenswert. Diese Async-Aufgabe verfügt nun über einen Fortschrittsdialog, der an die Aktivität angehängt ist, wenn Sie fragen, wie der Code angezeigt werden soll:

pDialog = new ProgressDialog(CLASS.this);

Sie übergeben den Class.thisas-Kontext an das Argument. Der Fortschrittsdialog ist also weiterhin an die Aktivität angehängt.

Stellen Sie sich nun das Szenario vor: Wenn wir versuchen, die Aktivität mit der Methode finish () zu beenden, während die asynchrone Aufgabe ausgeführt wird, ist dies der Punkt, an dem Sie versuchen, auf die an die Aktivität angehängte Ressource zuzugreifen, dh progress barwenn die Aktivität nicht mehr ausgeführt wird Dort.

Daher erhalten Sie:

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

Lösung dafür:

1) Stellen Sie sicher, dass das Dialogfeld geschlossen oder abgebrochen wird, bevor die Aktivität beendet wird.

2) Beenden Sie die Aktivität erst, nachdem das Dialogfeld geschlossen wurde, dh die asynchrone Aufgabe ist beendet.


1
Zu # 2: Sie haben nicht die volle Kontrolle darüber, wann Sie das beenden sollen Activity. Android kann es jederzeit beenden. # 2 macht also keinen Sinn.
Alexander Abakumov

6

Basierend auf der Antwort von @erakitin, aber auch kompatibel für Android-Versionen <API-Stufe 17. Leider wird Activity.isDestroyed () erst seit API-Stufe 17 unterstützt. Wenn Sie also genau wie ich auf eine ältere API-Stufe abzielen, müssen Sie dies tun überprüfe es selbst. Habe danach keine View not attached to window managerAusnahme mehr.

Beispielcode

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

verweisen Sie dies .


3

OnConfigurationChanged überschreiben und Fortschrittsdialog schließen. Wenn der Fortschrittsdialog im Hochformat erstellt und im Querformat geschlossen wird, wird die Ansicht nicht an den Fenstermanagerfehler angehängt.

Stoppen Sie auch den Fortschrittsbalken und die asynchrone Aufgabe in den Methoden onPause (), onBackPressed und onDestroy.

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

    asyncTaskObj.cancel(true);

}

3

Überschreiben Sie die Zerstörung der Aktivität und schließen Sie Ihren Dialog ab und machen Sie ihn null

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

3

Erstens ist der Absturzgrund, dass der Index von decorView -1 ist. Wir können ihn aus dem Android-Quellcode erkennen. Es gibt ein Code-Snippet:

Klasse: android.view.WindowManagerGlobal

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

Damit wir die folgende Auflösung erhalten, beurteilen Sie einfach den Index von decorView. Wenn er mehr als 0 beträgt, fahren Sie fort oder kehren Sie einfach zurück und geben Sie die Entlassung auf. Code wie folgt:

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

Überschreiben Sie die dismiss()Methode wie folgt :

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

Um das Problem zu reproduzieren, beenden Sie einfach die Aktivität, bevor Sie den Dialog schließen.


1

beste Lösung. Überprüfen Sie, ob der erste Kontext der Aktivitätskontext oder der Anwendungskontext ist. Wenn der Aktivitätskontext nur die Aktivität beendet ist oder nicht, rufen Sie dialog.show()oder aufdialog.dismiss();

Siehe Beispielcode unten ... hoffe, es wird hilfreich sein!

Dialogfeld anzeigen

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

Dialog schließen

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

Wenn Sie weitere Prüfungen hinzufügen möchten, fügen Sie eine Bedingung hinzu dialog.isShowing()oder dialog !-nullverwenden Sie sie &&.


0

Dieses Problem ist darauf zurückzuführen, dass Ihre Aktivität beendet ist, bevor die Entlassungsfunktion aufgerufen wird. Behandeln Sie die Ausnahme und überprüfen Sie Ihr ADB-Protokoll auf den genauen Grund.

/**
     * 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

Ich habe eine Möglichkeit, diese Ausnahme zu reproduzieren.

Ich benutze 2 AsyncTask. Einer erledigt eine lange Aufgabe und ein anderer eine kurze Aufgabe. Rufen Sie nach Abschluss der kurzen Aufgabe an finish(). Wenn eine lange Aufgabe abgeschlossen und aufgerufen wurde Dialog.dismiss(), stürzt sie ab.

Hier ist mein Beispielcode:

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

Sie können dies versuchen und herausfinden, wie Sie dieses Problem am besten beheben können. Aus meiner Studie geht hervor, dass es mindestens vier Möglichkeiten gibt, dies zu beheben:

  1. Antwort von @ erakitin: Aufruf isFinishing(), um den Status der Aktivität zu überprüfen
  2. Antwort von @ Kapé: Setzen Sie das Flag, um den Status der Aktivität zu überprüfen
  3. Verwenden Sie try / catch, um damit umzugehen.
  4. Rufen Sie AsyncTask.cancel(false)an onDestroy(). Dadurch wird verhindert, dass die Asynctask ausgeführt wird, onPostExecute()aber onCancelled()stattdessen ausgeführt.
    Hinweis: onPostExecute()Wird auch dann ausgeführt, wenn Sie AsyncTask.cancel(false)ein älteres Android-Betriebssystem wie Android 2.XX aufrufen

Sie können das beste für Sie auswählen.


0

Möglicherweise initialisieren Sie pDialog global, entfernen es dann und initialisieren Ihre Ansicht oder Ihren Dialog lokal. Ich habe das gleiche Problem, ich habe dies getan und mein Problem ist behoben. Hoffe es wird für dich funktionieren.


0

Wir haben auch unseren Dialog über onPauseMethode oder onDestroyMethode abgelehnt

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

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.