ProgressDialog: как предотвратить утечку окна
Я использую ProgressDialog для предотвращения взаимодействия пользователя, когда устройство загружает материал из Интернета.
все работало нормально, пока мой клиент не смог создать эту ошибку:
"07-06 17:10:50.363: ERROR/WindowManager(8821): Activity android.pixelrain.framework.PixelRainActivity has leaked window [email protected] 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 [email protected] 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);
}
});
Ответы
Ответ 1
Утечка происходит, вероятно, из вашего атрибута PixelRainActivity.staticThis
. Если вы ссылаетесь на активность, даже после того, как эта деятельность была уничтожена, у вас есть утечка памяти.
Самый простой способ исправить - использовать приложения Context
. Измените строку staticThis = this
в методе onCreate()
на staticThis = this.getApplicationContext()
, и он должен работать (и изменить тип staticThis
на Context
, если это еще не так)
Ответ 2
Использование:
progressDialog.dismiss();
в конце работы
Ответ 3
У cygnus есть хорошая идея использовать showDialog (MY_INT), где MY_INT - это просто какое-то постоянное значение, которое вы выбираете, чтобы отличить его от любых других подобных диалогов, которые вы запускаете таким образом. Вы снимаете его так же, как с помощью функции dispDialog (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);
}
Ответ 4
Вместо использования ProgressDialog.show()
попробуйте использовать
Activity.showDialog()
, который должен автоматически управлять Dialog
для вас и предотвращать утечки.
EDIT: когда вы вызываете showDialog(int)
, он вызывает Activity.onCreateDialog(int)
, где вы можете создать Dialog
, который вы хотите, и вернуть Dialog
, который хотите отобразить.
Ответ 5
Бывают ситуации, когда необходимо проверить в onDetach
или в onDestroy
, если диалог прогресса все еще отображается.
Например:
@Override
public void onDetach() {
if (mProgressDialog != null && mProgressDialog.isShowing())
mProgressDialog.dismiss();
super.onDetach();
}
Ответ 6
У меня возникла аналогичная проблема с диалоговым окном прогресса и фоновой задачей. AsyncTask (http://android-developers.blogspot.de/2009/05/painless-threading.html) позволил мне сделать гораздо более чисто и без просочившегося окна.
Ответ 7
Если вы используете любые thread
или AsyncTask
и загружаете материал из Интернета и показываете progress bar
, вы должны использовать DialogFragment
или отменить ход dialog
, когда Activity
получить остановку также, если вы показываете прогресс в AsyncTask
отменить отмену AsyncTask
и переопределить метод обратного вызова oncancel
и dismiss
диалог прогресса.
Window leak
в Activity
или fragment
на самом деле происходит из-за того, что вы пытаетесь добавить окно, и пока оно появляется, оно находится на foreground
, но когда вы нажимаете на него, он приостанавливается и затем останавливается через onStop
(). Таким образом, ваш CustomView
остается прикрепленным к окну, которое теперь исчезло. В соответствии с системой ваш CustomView
занял пространство, которое оно не освободило.
Ответ 8
Лучше использовать 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;
}
}
Ответ 9
Попробуйте вызвать progressDialog.dismiss()
до того, как действие будет убито. Я сделал это так.