【问题标题】:Activity has leaked window/dialog (this again!)Activity 泄露了窗口/对话框(又是这个!)
【发布时间】:2012-02-21 23:11:39
【问题描述】:

是的,我已经阅读了无数关于非常相似问题的问题。

我的代码很简单:我只是在onCreate 上使用showDialog(int id),然后旋转设备。 代码就是那个(测试用例),这足以导致问题。我的理解是showDialog 的方法会解决这个问题......对话框会消失,然后在更改后稍后会调用onCreate 并再次干净地显示对话框。但不是。 这个推理有什么问题?

我(想我)理解the cause,但我不知道如何解决。甚至 iosched 应用程序在执行 EULA 窗口时也存在同样的问题(更改 eula 对话框上的方向,您就会得到泄漏)。我已经阅读了关于在 onPause 上关闭对话框的信息,但是 1)我可能会在尚未显示时关闭对话框,并且 2)跟踪对话框似乎工作量太大。必须有更稳健的方法。

那么...处理该问题所需的更简洁的代码是什么?

谢谢。


日志错误输出:

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)

【问题讨论】:

  • 我看到有些人在跟踪窗口,基本上是在没有 showDialog 方法的情况下创建它们,并检查它们是否显示(isShowing()),然后相应地关闭(在 onStop/onPause) .但是,我认为自动方式 (showDialog) 应该更干净、更简单,这是一个安全的猜测,对吧?
  • 可以在onPostexecute()中关闭对话框,如dialog.dismiss();
  • 我会改用 DialogFragment。它自动管理旋转,因此更加优雅。
  • @WindRider 当然可以。我很久以前就离开了 showDialog 基础设施。这个问题是关于我仍然拥有支持 API3 的遗留代码(!!!),这让我觉得它没用。我很高兴它已经被弃用了很长时间。将它用于任何已经可以通过兼容性包使用片段的东西我会很疯狂。摆脱困境!

标签: android android-activity dialog window memory-leaks


【解决方案1】:

有一个内部类作为你的状态持有者,并有一个布尔字段指示你的对话框是否正在显示。使用onRetainNonConfigurationInstance 跟踪整个方向变化,并在onResume 上重新显示对话框

这是一些代码+伪代码:

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

}

【讨论】:

  • 嗯,不幸的是我说服自己showDialog 只是一个 PITA(对不起)。我会尝试这个关于手动对话框的解决方案,并会报告回来。谢谢!
  • 再次感谢您。我正在使用上述概念,并且效果很好!我什至在模拟器中非常快速地反复按 Ctrl+F11,每秒两次左右改变方向,很多次……这是普通手机用户永远不会做的事情。而且我的日志行尽可能干净。非常感谢,因为这让我昨天浪费了很多时间。
  • 很好的答案,我使用了onPause()
  • 同样的方法也适用于片段。使用setRetainInstance(true); 并在onDetach() / onAttach() 中存储/恢复对话框。
  • 但我还应该补充一点,正如大卫指出的那样,您现在应该使用基于片段的对话框类,这完全解决了这个问题。正如 Trinimon 所提到的,一定要setRetainInstance(true)。当您扩展 DialogFragment 类时,您可以在 onCreateDialog() 中执行此操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-16
  • 2016-06-26
  • 2020-06-13
  • 1970-01-01
相关资源
最近更新 更多