Search code examples
androidandroid-alertdialogweak-references

AlertDialog leaks despite using a WeakReference


I have a problem with creating AlertDialog inside a Handler, because it causes memory leaks or another errors.

Details:

I have an Activity, Thread and Handler with a WeakReference to an activity. Before I start my thread, I create ProgressDialog dialog in my activity class. Thread task have a reference to MyHandler object. When user dismisses dialog in my activity, the onCancelListener calls thread's interrupt(). My thread finishes safely task and send Message DOWNLOAD_STATE.CANCELLED to main activity thread. Then I cretate AlertDialog with code inside MyHandler

Problem:

The problem is when the user press a back button immediately after dismissing alert (which cancells thread) and before new AlertDialog has been created (inside MyHandler class). It tooks about second. When user doesn't press back in this time, everything is working.

It leaks here (inside MyHandler class): new AlertDialog.Builder(activity).setMessage("update cancelled").setPositiveButton("OK", null).show();

The normal thing is when user presses the back button, activity is destroyed (effect of activity onBackKeyPressed), but why handleMessage isn't stopped and creates AlertDialog?

My handler class:

static class MyHandler extends Handler {

    WeakReference<MainActivity> activityRef;
    MyHandler(MainActivity activity)
    {
        this.activityRef=new WeakReference<MainActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {

        MainActivity activity=activityRef.get();
        if(activity==null)
            return;

        if (msg.arg1==DownloadTask.DOWNLOAD_STATE.FINISHED.ordinal())
        {
            activity.dialog.dismiss();
            Toast.makeText(activity, "updated", Toast.LENGTH_LONG).show();
        }

        else if( msg.arg1== DownloadTask.DOWNLOAD_STATE.CANCELLED.ordinal())
        {
            new AlertDialog.Builder(activity).setMessage("update cancelled").setPositiveButton("OK", null).show();
        }

        else if(msg.arg1==DownloadTask.DOWNLOAD_STATE.ERROR.ordinal())
        {

              activity.dialog.dismiss();
            new AlertDialog.Builder(activity).setMessage(activity.getString(R.string.no_connection_info)).setPositiveButton("OK", null).show();
        }

    }
}

Inside thread's runnable:

if(Thread.interrupted()) 
        {
            Message msg=handler.obtainMessage();
            msg.arg1=DOWNLOAD_STATE.CANCELLED.ordinal();
            msg.arg2=counter;
            msg.obj=cities.length;
            handler.sendMessage(msg);

            return;
        }

Error stack trace1:

android.view.WindowLeaked: Activity com.mycompany.mooz.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@42723848 that was originally added here
        at android.view.ViewRootImpl.<init>(ViewRootImpl.java:403)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:311)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
        at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
        at android.view.Window$LocalWindowManager.addView(Window.java:554)
        at android.app.Dialog.show(Dialog.java:277)
        at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902)
        at com.mycompany.mooz.MainActivity$MyHandler.handleMessage(MainActivity.java:55)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4921)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
        at dalvik.system.NativeStart.main(Native Method)

Error stack trace 2:

android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@427329e0 is not valid; is your activity running?
        at android.view.ViewRootImpl.setView(ViewRootImpl.java:700)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:345)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
        at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
        at android.view.Window$LocalWindowManager.addView(Window.java:554)
        at android.app.Dialog.show(Dialog.java:277)
        at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902)
        at com.mycompany.mooz.MainActivity`enter code here`$MyHandler.handleMessage(MainActivity.java:55)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4921)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
        at dalvik.system.NativeStart.main(Native Method)

Solution

  • Activity com.mycompany.mooz.MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@42723848 that was originally added here

    it usually happens when the Activity is being paused and the dialog is still at screen. Keep a reference to your Dialog and call dismiss in onPause if the reference is not null and the dialog isShowing

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

    android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@427329e0 is not valid; is your activity running?

    this usually happens when you try to show a dialog while the Activity is being paused. To avoid check the isFinishing flag

      if (activity != null && !activity.isFinishing()) {
          // show dialog
      }