ProgressDialog:如何防止Window泄漏

50

我正在使用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);
            }
        });

2
我曾经遇到过同样的问题,只有在活动暂停时打开“Dialog”/“ProcessDialog”才会出现。你是这种情况吗? - Wroclai
1
或者可能在改变方向时? - Kumar Bibek
请更新被接受的答案,以使用“dismiss”。 - yonix
11个回答

119

使用:

progressDialog.dismiss();

工作结束


2
我错误地调用了hide而不是dismiss()。谢谢。 - Federico Ponzi
1
为了添加答案:你可能应该在onDestroy()中调用progressDialog.dismiss()(但在调用super.onDestroy()之前)。 - tharkay

17

这个泄漏可能来自于你的 PixelRainActivity.staticThis 属性。如果你在活动(Activity)被销毁后继续保留对该活动的引用, 那么就会出现内存泄漏。

最简单的解决方法是改用应用程序的 Context。在方法 onCreate() 中,将 staticThis = this 改为 staticThis = this.getApplicationContext() 就可以解决问题(如果还没有这样做,还需要将staticThis的类型更改为Context)。


22
对我来说没有用。我在Async任务的onPreExecute()中使用progressDialog.show(),但是我遇到了以下错误:android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application。 - M. Usman Khan
19
为了创建对话框,您需要传递Activity而不是Context。因此,this.getApplicationContext() 是不正确的。 - G. Kh.
2
根据此,使用getApplicationContext在此情况下会引发错误:https://dev59.com/Rm025IYBdhLWcg3wyZIn - Koesh
4
-1 @Guillaume Brunerie - 你需要传递一个Activity实例作为上下文,因为该操作是在UI线程中进行的。因此,传递getApplicationContext()会引发异常。 - ShihabSoft

5

有些情况下,您需要在onDetachonDestroy中验证进度对话框是否仍然可见。可以像这样:

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

3

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);
}

2

不要使用ProgressDialog.show(),尝试使用Activity.showDialog(),它应该自动管理Dialog,并防止泄漏。

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


1
但是使用Activity.showDialog()时,我需要将一个ID传递给对话框,那么如何显示进度对话框? - Jason Rogers

1

最好使用AsyncTask在后台获取互联网上的内容。而且不需要传递静态上下文。活动

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;
  }
}

1

2
那个链接是个死链接。 - Deepak Negi

1
如果您正在使用任何线程AsyncTask从互联网下载东西并显示进度条,则应使用DialogFragment或在Activity停止时取消进度对话框。此外,如果您正在Asynctask中显示进度,则首先取消Asynctask并覆盖oncancel回调方法,并在那里dismiss进度对话框。 Activityfragment中的Window leak实际上是由于您正在尝试添加窗口,当它出现在前台时,但当您按下主页键时,它会被暂停,然后通过onStop停止。因此,您的CustomView仍附加在现在已经消失的窗口上。因此,根据系统,您的customView占用了它没有释放的空间。

1

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

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

1
我遇到了这个异常。
@Override
    protected void onResume() {
        super.onResume();//at this line
        showProgressDialog();
    }

这个修复方法对我有效:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接