【问题标题】:ProgressDialog : how to prevent Leaked WindowProgressDialog:如何防止泄漏窗口
【发布时间】:2011-09-30 16:15:37
【问题描述】:

我正在使用 ProgressDialog 来防止用户在设备从互联网下载内容时进行交互。

在我的客户设法产生这个错误之前一切正常:

"07-06 17:10:50.363: ERROR/WindowManager(8821): Activity android.pixelrain.framework.PixelRainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@463f3e50 that was originally added here
07-06 17:10:50.363: ERROR/WindowManager(8821): android.view.WindowLeaked: Activity android.pixelrain.framework.PixelRainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@463f3e50 that was originally added here
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.ViewRoot.<init>(ViewRoot.java:251)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.Window$LocalWindowManager.addView(Window.java:424)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.app.Dialog.show(Dialog.java:241)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.app.ProgressDialog.show(ProgressDialog.java:107)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.app.ProgressDialog.show(ProgressDialog.java:90)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.HTTPHelper.DraftHelper.getDraft(DraftHelper.java:70)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.online.OnlineRetriver.getDraft(OnlineRetriver.java:312)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.HTTPHelper.DraftButtonGL.loadDraft(DraftButtonGL.java:72)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.HTTPHelper.DraftButtonGL.isTouched(DraftButtonGL.java:89)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.opengl.views.game.QuickStartGL.touchEnded(QuickStartGL.java:160)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.game.GameHandler.onTouchEvent(GameHandler.java:277)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.pixelrain.opengl.GLSurfaceViewChipmunk.onTouchEvent(GLSurfaceViewChipmunk.java:27)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.View.dispatchTouchEvent(View.java:3765)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:944)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:944)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:944)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1701)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1116)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.app.Activity.dispatchTouchEvent(Activity.java:2093)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1685)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1802)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.os.Looper.loop(Looper.java:144)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at android.app.ActivityThread.main(ActivityThread.java:4937)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at java.lang.reflect.Method.invokeNative(Native Method)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at java.lang.reflect.Method.invoke(Method.java:521)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
07-06 17:10:50.363: ERROR/WindowManager(8821):     at dalvik.system.NativeStart.main(Native Method)"

我不知道如何解决这个问题。

任何想法是什么原因造成的以及如何解决它?

日志将错误追溯到这一行:

    progressDialog = ProgressDialog.show(PixelRainActivity.staticThis, "",PixelRainActivity.staticThis.getResources().getString( R.string.draftProgressMessage), true);

如果我改成这样能解决问题吗:

this.runOnUiThread(new Runnable() {
            public void run() {
                progressDialog = ProgressDialog.show(PixelRainActivity.staticThis, "",PixelRainActivity.staticThis.getResources().getString( R.string.draftProgressMessage), true);
            }
        });

【问题讨论】:

  • 我遇到了同样的问题,而且只有在我打开Dialog/ProcessDialog 活动暂停时才会出现。是这样吗?
  • 或者可能在改变方向的时候?
  • 请将接受的答案更新为使用dismiss

标签: android memory-leaks progressdialog


【解决方案1】:

用途:

progressDialog.dismiss();

结束工作

【讨论】:

  • 我错误地调用了 hide 而不是dismiss()。谢谢。
  • 添加答案:您可能应该在onDestroy() 中调用progressDialog.dismiss()(但在调用super.onDestroy() 之前)
【解决方案2】:

泄漏可能来自您的PixelRainActivity.staticThis 属性。如果您保留对某个 Activity 的引用,即使在该 Activity 被销毁之后,您也存在内存泄漏。

最简单的修复方法是改用应用程序的Context。将方法onCreate() 中的staticThis = this 行更改为staticThis = this.getApplicationContext(),它应该可以工作(如果还没有这种情况,请将staticThis 的类型更改为Context

【讨论】:

  • 对我不起作用。我在异步任务的 onPreExecute() 中使用 progressDialog.show()。我得到: android.view.WindowManager$BadTokenException: Unable to add window -- token null 不适用于应用程序
  • 为了创建Dialog,你必须传递的不是Context,而是Activity。所以,this.getApplicationContext() 不正确
  • 据此,在这种情况下使用getApplicationContext会抛出错误:stackoverflow.com/questions/5796611/…
  • -1 @Guillaume Brunerie - 您必须将 Activity 实例作为上下文传递,因为操作是在 UI 中进行的。因此传递 getApplicationContext() 将导致异常。
【解决方案3】:

在某些情况下,您必须在onDetachonDestroy 中验证进度对话框是否仍然可见。 像这样:

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

【讨论】:

    【解决方案4】:

    cygnus 有一个使用 showDialog(MY_INT) 的好主意,其中 MY_INT 只是您选择的一些常数值,用于将其与您以这种方式启动的任何其他类似对话框区分开来。你可以用同样的方法用dismissDialog(MY_INT) 把它记下来。只是不要从您的 onPause 方法启动它。您可能希望通过用户将要进行的活动的 onResume 方法来执行此操作。然后像这样覆盖该活动的 onCreateDialog 方法:

    @Override
    protected Dialog onCreateDialog(int id) {
        if(id == MY_INT) {
            ProgressDialog progressDialog = new ProgressDialog(this);
            progressDialog.setMessage("Your message string");
            return progressDialog;
        }
        return super.onCreateDialog(id);
    }
    

    【讨论】:

      【解决方案5】:

      不要使用ProgressDialog.show(),而是尝试使用

      Activity.showDialog() 应该会自动为您管理Dialog 并防止泄漏。

      编辑:当您调用showDialog(int) 时,它将触发Activity.onCreateDialog(int),您可以在其中创建您想要的Dialog 并返回您想要显示的Dialog

      【讨论】:

      • 但是 Activity.showDialog() 我需要将一个 ID 传递给一个对话框,那么如何显示进度对话框?
      【解决方案6】:

      我在进度对话框和后台任务中遇到了类似的问题。 AsyncTask (http://android-developers.blogspot.de/2009/05/painless-threading.html) 让我做的更干净,而且没有泄漏的窗口。

      【讨论】:

      • 那个链接是死胡同
      【解决方案7】:

      最好使用AsyncTask 在后台从 Internet 获取内容。并且没有必要传递静态上下文。和活动

      new YourAsyncTask(context).execute();
      

      像上面那样调用 AsyncTask

      private class YourAsynTask extends AsyncTask<String,Void,String>
      {
       private Context context;
       private ProgressDialog progressDialog;
      
       //pass context in constructor
        public YourAsynTask(Context context)
        {
           this.context = context;
        }
      
        //show progress in onPre 
        @Override
        protected void onPreExecute()
        {
          //show Progress code here.
          progressDialog = ProgressDialog.show(context, "", "Loading. Please wait...", true);
        }
      
        //dismiss Progress dialog in onPost
        @Override 
        protected void OnPostExecute(String response)
        {
          if(progressDialog!=null)
           progressDialog.dismiss();
           progressDialog = null;
        }
      }
      

      【讨论】:

        【解决方案8】:

        如果您正在使用任何 threadAsyncTask 并从 Internet 下载内容并显示 progress bar ,您应该使用 DialogFragment 或取消进度 dialogActivity 停止时,如果您正在显示进度在Asynctask 拳头中取消Asynctask 并覆盖oncancel 回调方法和dismiss 那里的进度对话。

        Activityfragment 中的Window leak 实际上是由于您尝试添加一个窗口而发生的,当它显示时它位于foreground 上,但是当您按下主页时它会暂停并且然后通过onStop() 停止。所以你的CustomView 仍然附着在现在已经消失的窗口上。因此根据系统,你的customView 占据了它没有释放的空间。

        【讨论】:

          【解决方案9】:

          当另一个活动进入前台时,只需调用 cancel() 方法。

          @Override
          protected void onStop() {
             super.onStop();
             this.mProgress.cancel();
          }
          

          【讨论】:

            【解决方案10】:

            我遇到了这个异常

            @Override
                protected void onResume() {
                    super.onResume();//at this line
                    showProgressDialog();
                }
            

            这个修复对我有用:

              @Override
                    protected void onResume() {
                        showProgressDialog();
                        super.onResume();
                    }
            

            【讨论】:

              【解决方案11】:

              尝试在活动被终止之前调用progressDialog.dismiss()。我是这样修好的。

              【讨论】:

              • 在显示进度对话框之前,最好通过activity != null &amp;&amp; !activity.isFinishing()检查活动是否处于活动状态
              猜你喜欢
              • 2011-08-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-12-20
              • 2022-11-09
              相关资源
              最近更新 更多