ProgressDialog:如何防止泄漏的窗口
我正在使用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); } });
泄漏可能来自您的PixelRainActivity.staticThis
属性。 如果您保留对某个活动的引用,即使此活动已被破坏,您也有内存泄漏。
最简单的方法是使用应用程序的Context
来代替。 改变你的staticThis = this
方法在onCreate()
staticThis = this
行staticThis = this.getApplicationContext()
,它应该工作(和改变staticThis
types的Context
如果这还没有的话)
使用:
progressDialog.dismiss();
在最后的工作
cygnus有一个好主意,就是使用showDialog(MY_INT),其中MY_INT只是你select的一个常量值,只是为了区别于其他类似的对话框。 你可以用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); }
而不是使用ProgressDialog.show()
,尝试使用
Activity.showDialog()
应该为你自动pipe理Dialog
并防止泄漏。
编辑:当你调用showDialog(int)
,它会触发Activity.onCreateDialog(int)
,你可以创build你想要的Dialog
,并返回你想要显示的Dialog
。
我遇到了一个进度对话框和后台任务的类似问题。 AsyncTask( http://android-developers.blogspot.de/2009/05/painless-threading.html )使我可以做得更干净,没有泄露的窗口。
它更好地使用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; } }
如果你正在使用任何thread
或AsyncTask
和从互联网上下载的东西,并显示progress bar
,你应该使用DialogFragment
或取消进展dialog
时,如果你正在显示进度Asynctask
取消Asynctask
Activity
获取停止,并重写oncancel
callback方法,并dismiss
进度对话那里。
Activity
或fragment
Window leak
实际上是由于您正在尝试添加一个窗口而出现的,并且显示它在foreground
,但是当您按住home时,窗口会暂停,然后通过onStop
()停止。 所以你的CustomView
保持连接到现在已经消失的窗口。因此根据系统,你的customView
占用了它没有释放的空间。
有些情况下,如果进度对话框仍然可见,则必须在onDetach
或onDestroy
进行validation。 像这样:
@Override public void onDetach() { if (mProgressDialog != null && mProgressDialog.isShowing()) mProgressDialog.dismiss(); super.onDetach(); }
在活动死亡之前尝试调用progressDialog.dismiss()
。 我得到了这样的固定。