2013-03-14 2 views
9

Ceci est un extrait de code de mon projet que je me sers pour apprendre Android:Une classe DialogFragment interne doit-elle être statique ou non?

private void enableLocationSettings() { 
    Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 
    startActivity(settingsIntent); 
} 

@SuppressLint("ValidFragment") 
public class EnableGpsDialogFragment extends DialogFragment { 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     return new AlertDialog.Builder(getActivity()) 
      .setTitle("Tytuł") 
      .setMessage("wiadomosc") 
      .setPositiveButton("odpal", new DialogInterface.OnClickListener() { 

       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        enableLocationSettings(); 
       } 

      }) 
      .create(); 
    } 
} 

Comme vous pouvez le voir, je dois ajouter @SuppressLint pour faire mon travail d'application, mais sur la guide cette annotation était pas nécessaire.

Qu'est-ce que je fais mal?

Voici mes importations:

import android.annotation.SuppressLint; 
import android.app.Activity; 
import android.app.AlertDialog; 
import android.app.Dialog; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.location.LocationManager; 
import android.os.Bundle; 
import android.os.Handler; 
import android.os.Message; 
import android.provider.Settings; 
import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.FragmentManager; 
import android.support.v4.app.DialogFragment; 
import android.view.Menu; 
import android.view.View; 
import android.widget.TextView; 
import android.widget.ToggleButton; 

Répondre

20

L'exemple n'a pas de ces annotations parce que la classe est dans son propre fichier. Cela signifie que c'est indépendant de l'activité qui utilise le fragment.

Dans votre cas, votre fragment se trouve dans une activité et n'utilise pas le modificateur statique. Cela signifie qu'il est lié à l'instance d'activité. Le fait que le fragment dépende de l'instance d'activité est une mauvaise idée, ces deux classes ont des styles de vie complexes (d'autant plus que les activités sont détruites et recréées assez souvent) et doivent être indépendantes les unes des autres.

Vous devez créer le modificateur EnableGpsDialogFragmentstatic.

public static class EnableGpsDialogFragment extends DialogFragment { 

Une classe static ne dépend pas de la instance de classe englobante, donc l'avertissement disparaîtra.

Pour plus d'informations, lisez le tutoriel Java au nested classes.

Modifier en réponse à votre édition: Maintenant que les classes ne dépendent pas de l'autre instance de, vous devrez sortir une instance de YourActivity afin que vous puissiez appeler enabledLocationSettings() une façon est par coulée et ne fonctionnera que si EnableGpsDialogFragment est utilisé uniquement par YourActivity:

@Override 
public void onClick(DialogInterface dialog, int which) { 
    enableLocationSettings(); 
} 

à

@Override 
public void onClick(DialogInterface dialog, int which) { 
    ((YourActivity)getActivity()).enableLocationSettings(); 
} 

Si ce fragment sera utilisé par plusieurs activités, vous devriez create an interface to be implemented by each Activity instead .

+0

Merci, mais l'ajout de modificateur statique à EnableGpsDialogFragment me force à ajouter un modificateur static à la variable privée enableLocationSettings(). Et si je l'ajoute une nouvelle erreur apparaît: Impossible de faire une référence statique à la méthode non-statique startActivity (Intent) du type Activité – szpic

+0

@szpic bien parce que votre ** edit ** à votre question inclut maintenant un appel à ' enableLocationSettings(); ' –

+0

Pour contourner cela, vous pouvez utiliser une référence faible à tout ce qui est nécessaire à partir de l'activité. Ensuite, avant de l'utiliser, vérifiez qu'il est toujours valide. Cela doit être défini chaque fois que l'activité est détruite/créée pour s'assurer qu'elle est valide. Pour ajouter à propos des fragments statiques: Ils peuvent fuir la mémoire dans ce contexte s'ils ne sont pas créés statiquement car il est possible pour eux de conserver une référence et une ancienne activité qui n'est plus accessible autrement et qui aurait dû être récupérée. –

1

Il ne devrait pas être! De mon point de vue, je ne veux pas que mon DialogFragment (votre NetworkConnectionError) soit statique parce que je veux pouvoir appeler des variables ou des méthodes de ma classe (Activity) qui le contient.
Donc, ce ne sera pas statique. Mais je ne veux pas générer memoryLeaks non plus.
Alors, quelle est la solution?
Simple, quand vous allez dans onStop, assurez-vous de tuer votre DialogFragment, c'est aussi simple que cela. Ainsi, le code ressemble à quelque chose comme ça:

public class CarActivity extends AppCompatActivity{ 

/** 
* The DialogFragment networkConnectionErrorDialog 
*/ 
private NetworkConnectionError networkConnectionErrorDialog ; 
//... your code ...// 
@Override 
protected void onStop() { 
    super.onStop(); 
    //invalidate the DialogFragment to avoid stupid memory leak 
    if (networkConnectionErrorDialog != null) { 
     if (networkConnectionErrorDialog .isVisible()) { 
      networkConnectionErrorDialog .dismiss(); 
     } 
     networkConnectionErrorDialog = null; 
    } 
} 
/** 
* The method called to display your dialogFragment 
*/ 
private void onDeleteCurrentCity(){ 
    FragmentManager fm = getSupportFragmentManager(); 
    networkConnectionErrorDialog =(DeleteAlert)fm.findFragmentByTag("networkError"); 
    if(networkConnectionErrorDialog ==null){ 
     networkConnectionErrorDialog =new DeleteAlert(); 
    } 
    networkConnectionErrorDialog .show(getSupportFragmentManager(), "networkError"); 
} 

Et cette façon, vous éviter les fuites de mémoire (car il est mauvais) et vous vous assurer ne disposez pas d'un fragment statique putain qui ne peut pas accéder aux champs et méthodes de votre activité. C'est la bonne façon de gérer ce problème, de mon point de vue.

+0

DialogFragments * devrait * être des classes statiques publiques avec un constructeur public non-op, aussi simple que cela. Si ce n'est pas le cas, le système ne pourra pas recréer le fragment si cela est nécessaire dans une situation de faible mémoire, etc. En outre, à partir de la bibliothèque de support v25 votre application se bloquera si vous essayez d'afficher un DialogFragment contraintes: 'java.lang.IllegalStateException: Fragment TestActivity $ TestDialogFrament doit être une classe statique publique pour être correctement recréée à partir de l'état de l'instance.' – JHH

+0

oups, typo: no-op -> no-arg – JHH

+0

Non. Ils ne devraient pas . Je peux les recréer moi-même. –

3

Tous les DialogFragments doivent être publics et, si la classe interne est statique. Ils devraient aussi avoir un constructeur sans-argument public, et ne compter que sur setArguments() pour le passage de paramètres. Si vous ne respectez pas ce principe, vous avez produit un avertissement de peluches que vous pouviez supprimer si vous le souhaitiez, mais à partir de la bibliothèque de support Android v25, vous obtiendrez une exception si vous essayez d'afficher un DialogFragment qui ne se conforme pas à ces règles:

java.lang.IllegalStateException: Fragment TestActivity$TestDialogFrament must be a public static class to be properly recreated from instance state.

la raison en est, comme indiqué, que le système d'exploitation doit être capable de recréer tous les fragments dans le cas où quelque chose comme une situation de faible mémoire, il oblige à détruire des fragments quand une application est mise en arrière-plan. Lorsque l'application est à nouveau mise au premier plan, les fragments doivent pouvoir être recréés à partir de l'état de l'application sérialisée, ce qui n'est pas possible pour les classes internes non statiques autres que celles d'une instance de la classe externe La création n'est pas faite à partir de ce contexte. Malheureusement, cela rend les choses plus complexes pour les boîtes de dialogue, car il est généralement très pratique de créer simplement une sous-classe anonyme qui remplace onCreateDialog. Cependant, un tel dialogue ne serait pas capable de recréer du tout.

+0

Les situations de faible mémoire sont une menace réelle puisque nous avons au moins 2 Go de RAM dans les smartphones bon marché ... :) Je n'ai jamais compris pourquoi Google veut recréer mes fragments de dialogue s'ils devaient les détruire - je peux le faire seul Je le fais déjà (j'utilise v23 et ne mettra pas à niveau). –

+0

Eh bien, en partie parce que de nombreuses applications se comportent mal et sont en cours d'exécution en permanence tout le temps, ou sont constamment redémarrés en raison de fréquentes intentions de diffusion. Cela est sévèrement abordé dans Android N et O. Si 50 applications veulent s'exécuter tout le temps, il y aura beaucoup de swap in et out applications de la mémoire. De plus, n'oubliez pas que sur un écran 1080p, une seule bitmap est de 8 Mo. Et je ne suis pas sûr de comprendre pourquoi vous voudriez re-créer vous-même complètement manuellement, mais nous avons tous nos friandises ... – JHH

Questions connexes