Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Activity has leaked window/dialog (this again!)

Yes, I've read the countless questions regarding the very same problem.

My code is simple: I just use showDialog(int id) on the onCreate, and then I rotate the device. The code is just that (test case), and that's enough to cause the problem. It was my understanding that showDialog's methods would take care of that... the dialog would disappear and then the onCreate would be called later after the change and show the dialog again, cleanly. But no. What's wrong with this reasoning?

I (think I) understand the cause, but I don't know how to solve that. Even the iosched app has the same problem with their implementation of the EULA window (change orientation on the eula dialog and you get the leak). I've read about dismissing the dialog on onPause, but 1) I risk dismissing when it's not shown already, and 2) tracking the dialog seems too much work. There must be a more robust approach.

So... what's the cleaner code that is needed to handle that?

Thank you.


Log error output:

01-30 00:27:18.615: E/WindowManager(20316): Activity com.test.PreSetupActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@418e0c28 that was originally added here
01-30 00:27:18.615: E/WindowManager(20316): android.view.WindowLeaked: Activity com.test.PreSetupActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@418e0c28 that was originally added here
01-30 00:27:18.615: E/WindowManager(20316):     at android.view.ViewRootImpl.<init>(ViewRootImpl.java:343)
01-30 00:27:18.615: E/WindowManager(20316):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:245)
01-30 00:27:18.615: E/WindowManager(20316):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:193)
01-30 00:27:18.615: E/WindowManager(20316):     at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:118)
01-30 00:27:18.615: E/WindowManager(20316):     at android.view.Window$LocalWindowManager.addView(Window.java:537)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.Dialog.show(Dialog.java:274)
01-30 00:27:18.615: E/WindowManager(20316):     at com.test.PreSetupActivity.onCreate(PreSetupActivity.java:88)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.Activity.performCreate(Activity.java:4465)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3347)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.ActivityThread.access$700(ActivityThread.java:122)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1150)
01-30 00:27:18.615: E/WindowManager(20316):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-30 00:27:18.615: E/WindowManager(20316):     at android.os.Looper.loop(Looper.java:137)
01-30 00:27:18.615: E/WindowManager(20316):     at android.app.ActivityThread.main(ActivityThread.java:4340)
01-30 00:27:18.615: E/WindowManager(20316):     at java.lang.reflect.Method.invokeNative(Native Method)
01-30 00:27:18.615: E/WindowManager(20316):     at java.lang.reflect.Method.invoke(Method.java:511)
01-30 00:27:18.615: E/WindowManager(20316):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
01-30 00:27:18.615: E/WindowManager(20316):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
01-30 00:27:18.615: E/WindowManager(20316):     at dalvik.system.NativeStart.main(Native Method)
like image 243
davidcesarino Avatar asked Jan 30 '12 03:01

davidcesarino


1 Answers

Have an inner class which acts as your stateholder and have a boolean field in there which indicates whether or not your dialog is showing. Keep track of this across orientation changes using onRetainNonConfigurationInstance and just re-show the dialog on onResume

Here is some code+pseudo-code:

public class ProfileActivity extends Activity {
  private StateHolder mStateHolder;
  private Dialog dialog;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Object retained = getLastNonConfigurationInstance();
    if (retained != null && retained instanceof StateHolder) {
      mStateHolder = (StateHolder) retained;
    } else {
      mStateHolder = new StateHolder();
    }
  }

  @Override
  public Object onRetainNonConfigurationInstance() {
    return mStateHolder;
  }

  @Override
  public void onPause() {
    super.onPause();
    if(dialog != null && dialog.isShowing()) {
      dialog.dismiss();
    }
  }

  @Override
  public void onResume() {
    if(mStateHolder.mIsShowingDialog) {
      dialog.show();
    }
  }

  private void showDialog() {
    mStateHolder.mIsShowingDialog = true;
    dialog.show();
  }

  private static class StateHolder {
    boolean mIsShowingDialog;
    public StateHolder() {}
  }

}
like image 184
Cody Caughlan Avatar answered Nov 15 '22 21:11

Cody Caughlan