219

I am using ACRA to report app crashes. I was getting a View not attached to window manager error message and thought I had fixed it by wrapping the pDialog.dismiss(); in an if statement:

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

It has reduced the amount of View not attached to window manager crashes I recieve, but I am still getting some and I am not sure how to solve it.

Error message:

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 snippet:

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>

What am I missing to stop this crash from happening?

Manuel Allenspach
  • 12,467
  • 14
  • 54
  • 76
Howli
  • 12,291
  • 19
  • 47
  • 72
  • 1
    Did you ever figure this one out? I'm having the same problem. Can't seem to figure it out. – tomjung Apr 30 '14 at 21:17
  • Unfortunately no. I might start a bounty in a bit. You should check out some of the other threads dealing with thus problem in case they help you. – Howli Apr 30 '14 at 21:51
  • Your `AsyncTask` is declared inside `Activity` or `Fragment`? – erakitin May 10 '14 at 19:29
  • Please post the functions "doMoreStuff()" and "something()". – berserk May 15 '14 at 13:48
  • The issue may because of too much work of main thread, so try to use handlers to show the progressdialog, and if (pDialog!=null) this line is no need, because isShowing itself check whether the dialog is in progress or not. – Madhu May 16 '14 at 03:47

20 Answers20

504

How to reproduce the bug:

  1. Enable this option on your device: Settings -> Developer Options -> Don't keep Activities.
  2. Press Home button while the AsyncTask is executing and the ProgressDialog is showing.

The Android OS will destroy an activity as soon as it is hidden. When onPostExecute is called the Activity will be in "finishing" state and the ProgressDialog will be not attached to Activity.

How to fix it:

  1. Check for the activity state in your onPostExecute method.
  2. Dismiss the ProgressDialog in onDestroy method. Otherwise, android.view.WindowLeaked exception will be thrown. This exception usually comes from dialogs that are still active when the activity is finishing.

Try this fixed 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);
        }
    }
}
Marlon
  • 1,839
  • 2
  • 19
  • 42
erakitin
  • 11,437
  • 5
  • 44
  • 49
  • 17
    Great answer! BTW, isShowing() is unnecessary because dismiss() will do nothing if isShowing() == false. [Source Code](http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/5.0.0_r2-robolectric-0/android/app/Dialog.java#Dialog.dismissDialog%28%29) – Peter Zhao Jun 29 '15 at 12:23
  • 3
    What is the benefit of using `isDestroyed()` over `isFinishing()` on all APIs for this specific purpose? – Alexander Abakumov Sep 19 '17 at 19:25
  • 1
    @AlexanderAbakumov: From what I understand `isFinishing()` is not guaranteed to be `true` if the activity is destroyed by the system, see the [documentation on Activities](https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle). – Markus Penguin Feb 27 '18 at 08:05
  • 1
    @MarkusPenguin: Right. But, in this case, if one attempted to use an advice from author's comment `// or call isFinishing() if min sdk version < 17`, he runs into the very same exception. So, we need a different solution than this answer for apps running on API < 17. – Alexander Abakumov Apr 04 '18 at 22:06
  • 1
    @AlexanderAbakumov I'm having the same problem, but isFinishing ain't working for me, It worked saving a WeakReference to my activity inside the AsyncCallback, and then : `myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()` – rusito23 Sep 17 '18 at 15:17
  • @rusito23, cool! I just wanted to know any rationale behind using `isDestroyed()` instead of `isFinishing()`. Almost a year ago, still not sure what is the benefit and, unfortunately, no feedback from @erakitin. – Alexander Abakumov Sep 17 '18 at 15:53
  • Consider using the lifecycle android arch lib and do something similar to https://kinnrot.github.io/android-lifecycle-aware-modal/ – Chen Kinnrot Nov 08 '18 at 13:23
  • In my project we had use a Handler for fingerprint. In this case, we had to pass the activity that calls the handler at the instance set. For that, we can use the followed verification: //activityRoot is the activity that I passed when I instanced it. if (activityRoot == null || activityRoot.isDestroyed()) { return; } – Rafael Baptista Mar 27 '19 at 13:58
  • Can anyone explain this answer in detail – Sam Jul 04 '19 at 13:28
40

Issue could be that the Activity have been finished or in progress of finishing.

Add a check isFinishing , and dismiss dialog only when this is false

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

isFinishing : Check to see whether this activity is in the process of finishing,either because you called finish on it or someone else has requested that it finished.

Libin
  • 16,967
  • 7
  • 61
  • 83
13

For Dialog created in a Fragment, I use the following code:

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

I use this pattern to deal with the case when a Fragment may be detached from the Activity.

Teng-pao Yu
  • 1,313
  • 15
  • 30
8

See how the Code is working here:

After calling the Async task, the async task runs in the background. that is desirable. Now, this Async task has a progress dialog which is attached to the Activity, if you ask how to see the code:

pDialog = new ProgressDialog(CLASS.this);

You are passing the Class.this as context to the argument. So the Progress dialog is still attached to the activity.

Now consider the scenario: If we try to finish the activity using the finish() method, while the async task is in progress, is the point where you are trying to access the Resource attached to the activity ie the progress bar when the activity is no more there.

Hence you get:

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

Solution to this:

1) Make sure that the Dialog box is dismissed or canceled before the activity finishes.

2) Finish the activity, only after the dialog box is dismissed, that is the async task is over.

Kailas
  • 7,350
  • 3
  • 47
  • 63
7

Based on @erakitin answer, but also compatible for Android versions < API level 17. Sadly Activity.isDestroyed() is only supported since API level 17, so if you're targeting an older API level just like me, you'll have to check it yourself. Haven't got the View not attached to window manager exception after that.

Example code

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é
  • 4,411
  • 3
  • 37
  • 54
6
@Override
public void onPause() {
    super.onPause();

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

refer this.

Community
  • 1
  • 1
Meghna
  • 539
  • 4
  • 14
3

Override onConfigurationChanged and dismiss progress dialog. If progress dialog is created in portrait and dismisses in landscape then it will throw View not attached to window manager error.

Also stop the progress bar and stop the async task in onPause(), onBackPressed and onDestroy method.

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

    asyncTaskObj.cancel(true);

}
Hemant Patel
  • 3,160
  • 1
  • 20
  • 29
3

Override onDestroy of the Activity and Dismiss your Dialog & make it null

protected void onDestroy ()
    {
        if(mProgressDialog != null)
            if(mProgressDialog.isShowing())
                mProgressDialog.dismiss();
        mProgressDialog= null;
    }
Yogesh Rathi
  • 6,331
  • 4
  • 51
  • 81
surya
  • 607
  • 5
  • 18
3

Overide the dismiss() method like this:

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

To reproduce the issue, just finish activity before dismiss dialog.

thundertrick
  • 1,634
  • 1
  • 17
  • 22
2

Firstly,the crash reason is decorView's index is -1,we can knew it from Android source code ,there is code snippet:

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

so we get follow resolution,just judge decorView's index,if it more than 0 then continue or just return and give up dismiss,code as follow:

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();
        }
Community
  • 1
  • 1
aolphn
  • 2,950
  • 2
  • 21
  • 30
1

This issue is because of your activity gets finished before the dismiss function gets called. Handle the exception and check your ADB log for the exact reason.

/**
     * 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
  • 504
  • 7
  • 15
1

I got a way to reproduce this exception.

I use 2 AsyncTask. One do long task and another do short task. After short task complete, call finish(). When long task complete and call Dialog.dismiss(), it crashes.

Here's my sample code:

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

You can try this and find out what is the best way to fix this issue. From my study, there are at least 4 ways to fix it:

  1. @erakitin's answer: call isFinishing() to check activity's state
  2. @Kapé's answer: set flag to check activity's state
  3. Use try/catch to handle it.
  4. Call AsyncTask.cancel(false) in onDestroy(). It will prevent the asynctask to execute onPostExecute() but execute onCancelled() instead.
    Note: onPostExecute() will still execute even you call AsyncTask.cancel(false) on older Android OS, like Android 2.X.X.

You can choose the best one for you.

1

best solution. Check first context is activity context or application context if activity context then only check activity is finished or not then call dialog.show() or dialog.dismiss();

See sample code below... hope it will be helpful !

Display dialog

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

Dismiss dialog

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

If you want to add more checks then add dialog.isShowing() or dialog !-null using && condition.

Community
  • 1
  • 1
Yogesh Rathi
  • 6,331
  • 4
  • 51
  • 81
1

we have also dismiss our dialog on onPause method or onDestroy method

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

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
Siddharth
  • 9,349
  • 16
  • 86
  • 148
0

May be you initialize pDialog globally, Then remove it and intialize your view or dialog locally.I have same issue, I have done this and my issue is resolved. Hope it will work for u.

Shashwat Gupta
  • 876
  • 9
  • 22
0

You can try to check this solution or try the below solution.

if (pDialog instanceof Activity && !((Activity) mContext).isFinishing())
        pDialog.show();
MohamedHarmoush
  • 1,033
  • 11
  • 17
0

Check if the owner activity is still alive:

if (dialog.getOwnerActivity() ==null || dialog.getOwnerActivity().isFinishing()) {
    dialog.dismiss();
}
Gil SH
  • 3,789
  • 1
  • 27
  • 25
  • 2
    This is actually wrong, the checks should be opposite. Dialog should have the owner activity to dismiss, not the wise versa. – Gunhan Jun 22 '22 at 15:08
0

I'm using this simple extension function to check whether the activity is destroyed or not.

    protected fun Activity?.isNoMore(): Boolean {
        return this?.let {
            isFinishing || isDestroyed
        } ?: true
    }

Use it in your activity as mentioned below.

if (!isNoMore()) {
//show your dialog. Also, make the null check for dialog object.
}
Vishal Naikawadi
  • 419
  • 6
  • 11
0

None of the suggested solutions worked for me, because im using custom Dialog class and i dont want to override stop/destroy method in all my activities, is easier add one more parameter to my class and send it from the instances, this solution works for me

class CustomLoadingClass(var activity: Activity?, var lifecycle: Lifecycle? = null) {

lateinit var dialog: Dialog

fun show(cancelable: Boolean = false, title: String? = null) {
    lifecycle?.let {
        val defaultLifecycleObserver = object : DefaultLifecycleObserver {
            override fun onCreate(owner: LifecycleOwner) {
                super.onCreate(owner)
                activity?.let { it ->
                    if (!it.isFinishing && !it.isDestroyed) {
                        dialog = Dialog(it, android.R.style.Theme_Translucent_NoTitleBar)
                        dialog.setContentView(it.layoutInflater.inflate(R.layout.full_screen_progress_bar,null)!!)
                        dialog.setCancelable(cancelable)
                        dialog.show()
                    }
                }
            }
            override fun onPause(owner: LifecycleOwner) {
                super.onPause(owner)
                dismiss()
            }

            override fun onStop(owner: LifecycleOwner) {
                super.onStop(owner)
                dismiss()
            }

            override fun onDestroy(owner: LifecycleOwner) {
                super.onDestroy(owner)
                dismiss()
            }
        }
        it.addObserver(defaultLifecycleObserver)
    }
}

fun dismiss() {
    activity?.let { it ->
         if (!it.isFinishing && !it.isDestroyed) 
           dialog.dismiss()
    }
  }
}
0

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