15

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)
Community
  • 1
  • 1
davidcesarino
  • 16,160
  • 16
  • 68
  • 109
  • I see some people are keeping track of windows, basically creating them without the `showDialog` method, and checking if they are displayed (`isShowing()`), and then dismissing accordingly (at onStop/onPause). However, I believe it's a safe guess to tell that the automatic way (showDialog) should be cleaner and easier, right? – davidcesarino Jan 30 '12 at 03:56
  • you can close dialog in onPostexecute(),like dialog.dismiss(); – user3114805 Sep 06 '13 at 05:36
  • I would go with DialogFragment instead. It manages the rotation automatically and thus is more elegant. – WindRider Nov 27 '13 at 10:31
  • @WindRider sure, of course. I've moved away from the showDialog infrastructure a long time ago. That question regarded legacy code I still have that supports API3 (!!!), which made me feel like it was useless. I'm glad it has been deprecated for a long, long time now. I would be crazy to use it for anything that can already use fragments through the compatibility packages. Good riddance! – davidcesarino Nov 27 '13 at 19:41

1 Answers1

13

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

}
Cody Caughlan
  • 32,456
  • 5
  • 63
  • 68
  • Hmmm, unfortunately I'm convincing myself that `showDialog` is just a PITA (sorry). I'll try this solution regarding manual dialogs and will report back. Thank you! – davidcesarino Jan 30 '12 at 04:41
  • 1
    Thank you again. I'm using the above concept and it's working perfectly! I even repeatedly pressed Ctrl+F11 very quickly in the emulator, changing the orientation around twice per second, many times... something that a user in a normal phone would never do. And my log lines were as clean as it could be. Thank you very much as this made me lose quite some time yesterday. – davidcesarino Jan 31 '12 at 01:26
  • Great answer, I used the `onPause()` – rahstame Sep 10 '13 at 10:10
  • Same Approach works with fragments as well. Used `setRetainInstance(true);` and stored / restored dialogs in `onDetach()` / `onAttach()`. – Trinimon Feb 10 '14 at 11:03
  • Remember everyone: just like I stated in the question comment area, `showDialog` has been deprecated for a long time now, and probably for that reason, too. This is not even an issue anymore if you're using a proper way to show dialogs (e.g., the Fragment-based dialog classes). I'd consider this question obsolete. – davidcesarino Jul 11 '14 at 18:17
  • Nice example, Cody. Note that for newer Android API levels, you may want to extend `FragmentActivity` rather than `Activity`, and use `onRetainCustomNonConfigurationInstance()` and `getLastCustomNonConfigurationInstance()` because `getLastNonConfigurationInstance()` is now deprecated. – Steve Liddle Jul 15 '14 at 20:19
  • 1
    But I should also add, as David pointed out, that you ought to use fragment-based dialog classes now, which fully resolves the issue. As Trinimon mentioned, be sure to `setRetainInstance(true)`. You do this in `onCreateDialog()` when you extend the `DialogFragment` class. – Steve Liddle Jul 15 '14 at 23:38