ProgressDialog:如何防止泄漏窗口

Posted

技术标签:

【中文标题】ProgressDialog:如何防止泄漏窗口【英文标题】:ProgressDialog : how to prevent Leaked Window 【发布时间】: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 【参考方案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会抛出错误:***.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()检查活动是否处于活动状态

以上是关于ProgressDialog:如何防止泄漏窗口的主要内容,如果未能解决你的问题,请参考以下文章

ProgressDialog.show() - Activity 已泄露窗口

如何防止java中的内存泄漏

如何防止 CompileAssemblyFromSource 泄漏内存?

Echarts 如何防止内存泄漏

如何在闭包中使用 self 来防止内存泄漏

用任务管理器杀死时如何防止内存泄漏[重复]