Mon activité tente de créer un AlertDialog qui nécessite un contexte en tant que paramètre. Cela fonctionne comme prévu si j'utilise:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
Cependant, je suis réticent à utiliser "ceci" en tant que contexte en raison du risque de fuite de mémoire lorsque Activity est détruit et recréé, même lors d'une opération simple, telle qu'une rotation d'écran. D'un article lié sur le blog du développeur Android) :
Il existe deux moyens simples d'éviter les fuites de mémoire liées au contexte. La plus évidente consiste à éviter d'échapper au contexte en dehors de son propre champ d'application. L'exemple ci-dessus montre le cas d'une référence statique, mais les classes internes et leur référence implicite à la classe externe peuvent être tout aussi dangereuses. La deuxième solution consiste à utiliser le contexte d'application. Ce contexte restera actif aussi longtemps que votre application sera active et ne dépendra pas du cycle de vie des activités. Si vous envisagez de conserver des objets de longue durée nécessitant un contexte, souvenez-vous de l'objet d'application. Vous pouvez l'obtenir facilement en appelant Context.getApplicationContext () ou Activity.getApplication ().
Mais pour la AlertDialog()
, ni getApplicationContext()
ni getApplication()
ne sont acceptables en tant que contexte, car ils lèvent l'exception:
"Impossible d'ajouter la fenêtre - le jeton null n'est pas pour une application"
par références: 1 , 2 , , etc.
Donc, cela devrait-il vraiment être considéré comme un "bug", puisqu'il nous est officiellement conseillé d'utiliser Activity.getApplication()
et que cela ne fonctionne pas comme annoncé?
Jim
Au lieu de getApplicationContext()
, utilisez simplement ActivityName.this
.
L'utilisation de this
n'a pas fonctionné pour moi, mais MyActivityName.this
l'a été. J'espère que cela aidera tous ceux qui ne pourraient pas obtenir this
au travail.
Vous pouvez continuer à utiliser getApplicationContext()
, mais avant de l'utiliser, vous devez ajouter l'indicateur: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
et l'erreur ne s'affichera pas.
Ajoutez l’autorisation suivante à votre manifeste:
<uses-permission Android:name="Android.permission.SYSTEM_ALERT_WINDOW" />
Vous avez correctement identifié le problème lorsque vous avez déclaré "... pour AlertDialog () ni getApplicationContext () ni getApplication () n'est acceptable en tant que contexte, car il renvoie l'exception suivante:" Impossible d'ajouter une fenêtre - le jeton null n'est pas valable. une application'"
Pour créer un dialogue, vous avez besoin d'un contexte d'activité ou d'un contexte de service . , pas un contexte d'application (getApplicationContext () et getApplication () renvoient un contexte d'application).
Voici comment vous obtenez le contexte d'activité :
(1) Dans une activité ou un service:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
(2) Dans un fragment: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
Les fuites de mémoire ne sont pas un problème intrinsèque à la référence "this", qui est la référence d'un objet à lui-même (c'est-à-dire une référence à la mémoire effectivement allouée pour stocker les données de l'objet). Il se produit toute mémoire allouée pour laquelle le Garbage Collector (GC) ne peut pas se libérer après que la mémoire allouée a dépassé sa durée de vie utile.
La plupart du temps, lorsqu'une variable sort du cadre, la mémoire est récupérée par le GC. Cependant, des fuites de mémoire peuvent se produire lorsque la référence à un objet contenu dans une variable, telle que "x", persiste même après que l'objet a dépassé sa durée de vie utile. La mémoire allouée sera donc perdue aussi longtemps que "x" contiendra une référence à celle-ci car GC ne libérera pas la mémoire tant que cette mémoire sera toujours référencée. Parfois, les fuites de mémoire ne sont pas apparentes à cause de une chaîne de références à la mémoire allouée. Dans ce cas, le CPG ne libérera pas la mémoire tant que toutes les références à cette mémoire n'auront pas été supprimées.
Pour éviter les fuites de mémoire, recherchez dans votre code les erreurs de logique qui font que la mémoire allouée est référencée indéfiniment par "this" (ou par d'autres références). N'oubliez pas de vérifier également les références de la chaîne. Voici quelques outils que vous pouvez utiliser pour vous aider à analyser l'utilisation de la mémoire et à trouver ces fuites mémoire fastidieuses:
Votre dialogue ne doit pas être un "objet à vie longue nécessitant un contexte". La documentation est déroutante. Fondamentalement, si vous faites quelque chose comme:
static Dialog sDialog;
(notez le statique)
Puis dans une activité quelque part que vous avez faite
sDialog = new Dialog(this);
Vous feriez probablement une fuite de l'activité initiale pendant une rotation ou une opération similaire qui détruirait l'activité. (Sauf si vous nettoyez dans onDestroy, mais dans ce cas, vous ne rendriez probablement pas l'objet Dialog statique)
Pour certaines structures de données, il serait judicieux de les rendre statiques et basées sur le contexte de l'application, mais généralement pas pour les éléments liés à l'interface utilisateur, tels que les dialogues. Donc, quelque chose comme ça:
Dialog mDialog;
...
mDialog = new Dialog(this);
C’est bien et ne devrait pas laisser fuir l’activité car mDialog serait libéré avec l’activité car elle n’est pas statique.
Je devais envoyer mon contexte via un constructeur sur un adaptateur personnalisé affiché dans un fragment et présentais ce problème avec getApplicationContext (). Je l'ai résolu avec:
this.getActivity().getWindow().getContext()
dans le rappel de fragments 'onCreate
.
dans Activité utilisez simplement:
MyActivity.this
dans fragment:
getActivity();
Dans Activity
au clic du bouton affichant une boîte de dialogue
Dialog dialog = new Dialog(MyActivity.this);
Travaillé pour moi.
Petit bidouillage: vous pouvez empêcher la destruction de votre activité par GC (vous ne devriez pas le faire, mais cela peut aider dans certaines situations. N'oubliez pas de définir contextForDialog
sur null
lorsqu'il n'est plus nécessaire):
public class PostActivity extends Activity {
...
private Context contextForDialog = null;
...
public void onCreate(Bundle savedInstanceState) {
...
contextForDialog = this;
}
...
private void showAnimatedDialog() {
mSpinner = new Dialog(contextForDialog);
mSpinner.setContentView(new MySpinner(contextForDialog));
mSpinner.show();
}
...
}
Vous devez passer this@YourActivity
au lieu de applicationContext
ou baseContext
Si vous utilisez un fragment et utilisez le message AlertDialog/Toast, utilisez getActivity () dans le paramètre context.
comme ça
ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();
J'utilisais ProgressDialog
dans un fragment et j'obtenais cette erreur en passant getActivity().getApplicationContext()
en tant que paramètre constructeur. Le changer en getActivity().getBaseContext()
ne fonctionnait pas non plus.
La solution qui a fonctionné pour moi était de passer getActivity()
; c'est à dire.
progressDialog = new ProgressDialog(getActivity());
ajouter
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
et
"Android.permission.SYSTEM_ALERT_WINDOW"/>
dans le manifeste
Ça marche pour moi maintenant. Après même fermer et ouvrir l'application, m'a donné l'erreur à ce moment-là.
Si vous êtes en dehors de l'activité, vous devez utiliser dans votre fonction "NameOfMyActivity.this" comme activité, par exemple:
public static void showDialog(Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage("Your Message")
.setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}
//Outside your Activity
showDialog(NameOfMyActivity.this);
Utiliser MyDialog md = new MyDialog(MyActivity.this.getParent());
Essayez d'utiliser le contexte d'une activité qui sera sous le dialogue. Mais soyez prudent lorsque vous utilisez "ce" mot clé, car il ne fonctionnera pas à chaque fois.
Par exemple, si vous avez TabActivity en tant qu'hôte avec deux onglets et que chaque onglet est une autre activité, et si vous essayez de créer une boîte de dialogue à partir de l'un des onglets (activités) et si vous utilisez "this", vous obtiendrez une exception, La boîte de dialogue de cas doit être connectée à l’activité hôte qui héberge tout et est visible. (vous pouvez dire le contexte d'activité parent le plus visible)
Je n'ai pas trouvé cette information dans aucun document, mais en essayant. C’est ma solution qui ne repose pas sur de solides antécédents. Si vous êtes plus connu, n'hésitez pas à commenter.
Si vous utilisez un fragment et utilisez un message AlertDialog / Toast
, utilisez getActivity()
dans le paramètre context.
Travaillé pour moi.
À votre santé!
Pour les futurs lecteurs, cela devrait aider:
public void show() {
if(mContext instanceof Activity) {
Activity activity = (Activity) mContext;
if (!activity.isFinishing() && !activity.isDestroyed()) {
dialog.show();
}
}
}
Je pense que cela peut également arriver si vous essayez d'afficher une boîte de dialogue à partir d'un fil qui n'est pas le fil principal de l'interface utilisateur.
Utilisez runOnUiThread()
dans ce cas.
Essayez getParent()
à la place du contexte comme neuve AlertDialog.Builder(getParent());
J'espère que cela fonctionnera, cela a fonctionné pour moi.
Ou une autre possibilité est de créer un dialogue comme suit:
final Dialog dialog = new Dialog(new ContextThemeWrapper(
this, R.style.MyThemeDialog));
Dans mon cas de travail:
this.getContext();
Après avoir examiné l'API, vous pouvez transmettre à votre boîte de dialogue votre activité ou obtenirActivité si vous êtes dans un fragment, puis nettoyez-la avec force avec dialog.dismiss () dans les méthodes de retour pour éviter les fuites.
Bien que cela ne soit explicitement indiqué nulle part à ma connaissance, il semble que la boîte de dialogue de OnClickHandlers vous a été renvoyée, rien que pour cela.
Voici comment j'ai résolu la même erreur pour mon application:
Ajouter la ligne suivante après la création de la boîte de dialogue:
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
Vous n'aurez pas besoin d'acquérir un contexte. Ceci est particulièrement utile si vous ouvrez une autre boîte de dialogue par-dessus une boîte de dialogue actuellement affichée. Ou quand ce n'est pas pratique d'avoir un contexte.
J'espère que cela pourra vous aider dans le développement de votre application.
David
Si votre dialogue est en train de créer sur l'adaptateur:
Transmettez l'activité au constructeur de l'adaptateur:
adapter = new MyAdapter(getActivity(),data);
Recevoir sur l'adaptateur:
public MyAdapter(Activity activity, List<Data> dataList){
this.activity = activity;
}
Maintenant, vous pouvez utiliser sur votre constructeur
AlertDialog.Builder alert = new AlertDialog.Builder(activity);