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】:在某些情况下,您必须在onDetach
或onDestroy
中验证进度对话框是否仍然可见。
像这样:
@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】:如果您正在使用任何 thread
或 AsyncTask
并从 Internet 下载内容并显示 progress bar
,您应该使用 DialogFragment
或取消进度 dialog
当 Activity
停止时,如果您正在显示进度在Asynctask
拳头中取消Asynctask
并覆盖oncancel
回调方法和dismiss
那里的进度对话。
Activity
或fragment
中的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 && !activity.isFinishing()
检查活动是否处于活动状态以上是关于ProgressDialog:如何防止泄漏窗口的主要内容,如果未能解决你的问题,请参考以下文章
ProgressDialog.show() - Activity 已泄露窗口