ProgressDialog: comment éviter les fuites de fenêtre

J’utilise ProgressDialog pour empêcher l’utilisateur d’interagir pendant que l’appareil télécharge des fichiers sur Internet.

tout fonctionnait bien jusqu’à ce que mon client parvienne à produire ce bug:

"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.(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.OnlineResortingver.getDraft(OnlineResortingver.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)" 

et je ne sais pas comment résoudre ce problème.

Des idées qui causent cela et comment le résoudre?

Le journal retrace l’erreur sur cette ligne:

  progressDialog = ProgressDialog.show(PixelRainActivity.staticThis, "",PixelRainActivity.staticThis.getResources().getSsortingng( R.ssortingng.draftProgressMessage), true); 

résoudrait-il le problème si je le changeais en ceci:

 this.runOnUiThread(new Runnable() { public void run() { progressDialog = ProgressDialog.show(PixelRainActivity.staticThis, "",PixelRainActivity.staticThis.getResources().getSsortingng( R.ssortingng.draftProgressMessage), true); } }); 

La fuite provient probablement de votre atsortingbut PixelRainActivity.staticThis . Si vous gardez une référence à une activité, même après que cette activité a été détruite, vous avez une fuite de mémoire.

La solution la plus simple consiste à utiliser le Context l’application à la place. Changez votre staticThis = this ligne dans la méthode onCreate() en staticThis = this.getApplicationContext() et cela devrait fonctionner (et changer le type de staticThis en Context si ce n’est pas déjà le cas)

Utilisation:

 progressDialog.dismiss(); 

en fin de travail

Il y a des situations où vous devez vérifier dans onDetach ou dans onDestroy si la boîte de dialog de progression est toujours visible. Ainsi:

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

cygnus a une bonne idée d’utiliser showDialog (MY_INT), où MY_INT est juste une valeur constante que vous choisissez juste pour le distinguer des autres dialogs similaires que vous lancez de cette façon. Vous le prenez de la même manière avec licencierDialog (MY_INT). Il suffit de ne pas le lancer à partir de votre méthode onPause. Vous souhaiterez peut-être le faire à la place de la méthode onResume de l’activité que l’utilisateur va effectuer. Ensuite, vous remplacez la méthode onCreateDialog de cette activité comme suit:

 @Override protected Dialog onCreateDialog(int id) { if(id == MY_INT) { ProgressDialog progressDialog = new ProgressDialog(this); progressDialog.setMessage("Your message ssortingng"); return progressDialog; } return super.onCreateDialog(id); } 

Au lieu d’utiliser ProgressDialog.show() , essayez d’utiliser

Activity.showDialog() qui devrait automatiquement gérer le Dialog pour vous et empêcher les fuites.

EDIT: Lorsque vous appelez showDialog(int) , cela déclenchera Activity.onCreateDialog(int) où vous pouvez créer la Dialog souhaitée et renvoyer la Dialog à afficher.

J’ai rencontré un problème similaire avec un dialog de progression et une tâche en arrière-plan. La AsyncTask ( http://android-developers.blogspot.de/2009/05/painless-threading.html ) m’a permis de faire les deux beaucoup plus proprement et sans la fenêtre qui fuyait.

Il est préférable d’utiliser un AsyncTask pour obtenir quelque chose d’Internet en arrière-plan. Et il n’est pas nécessaire de passer un contexte statique cependant. Et activité

 new YourAsyncTask(context).execute(); 

Appelez AsyncTask comme ci-dessus

 private class YourAsynTask extends AsyncTask { 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(Ssortingng response) { if(progressDialog!=null) progressDialog.dismiss(); progressDialog = null; } } 

Si vous utilisez un thread ou AsyncTask et que vous téléchargez des éléments sur Internet et que vous affichez progress bar , vous devez utiliser DialogFragment ou annuler la dialog lorsque l’ Activity est interrompue si vous annulez Asynctask fist annuler Asynctask et ignorer la méthode de rappel Là.

Window leak dans Activity ou le fragment se produit en réalité parce que vous essayez d’append une fenêtre et qu’elle apparaît au foreground , mais lorsque vous appuyez sur la onStop elle est interrompue via onStop (). Donc, votre CustomView rest attaché à la fenêtre qui a maintenant disparu. Selon le système, votre customView occupé l’espace qu’il n’a pas libéré.

Essayez d’appeler progressDialog.dismiss() avant que l’activité soit tuée. J’ai le mien comme ça.