web-dev-qa-db-fra.com

Variables membres vs setArguments dans les fragments

J'ai remarqué que dans la Android pour les fragments (notamment DialogFragment )), ils font deux ou trois choses différentes de ce à quoi je m'attendais:

1). Utilisez la méthode public static foo newInstance() plutôt qu'un constructeur.
2). Passez des valeurs à onCreateDialog en utilisant setArguments plutôt que des variables membres.

J'ai lu que newInstance semble être préférable lors de l'utilisation de la réflexion. Cependant, je ne comprends vraiment pas pourquoi ils passent des paramètres via un bundle. J'aurais pensé que l'utilisation de variables membres serait plus sûre (n'utilisant pas de chaîne pour extraire une carte) et aurait moins de surcharge.

Des pensées?

66
oobayly

J'ai également découvert cela et trouvé quelques avantages à utiliser les arguments Bundle sur les champs d'instance:

  • S'il se trouve dans un Bundle le Android le sait et peut créer et détruire votre Fragment (en utilisant le constructeur sans paramètre/par défaut obligatoire et les méthodes de cycle de vie habituelles) , et passez à nouveau l'ensemble d'arguments. De cette façon, aucun argument n'est perdu lors d'une vague de destruction de mémoire ou des changements d'orientation éventuels (cela me frappe souvent lors du premier déploiement sur un appareil réel après le développement dans l'émulateur le moins rotatif).

  • Vous pouvez simplement passer les extras Bundle d'un Activity tel quel à un Fragment incorporé dans la mise en page; par exemple. Je l'utilise souvent lorsque j'ai un Activity qui affiche un Fragment "plein écran" et a besoin d'un ID (ou ContentProvider URI) pour savoir quoi afficher/faire. J'ajoute parfois même plus de choses à un Bundle (ou une copie) avant de le transmettre, par ex.

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    
      if (savedInstanceState == null) { // not a re-creation
        final Bundle args = new Bundle(getIntent().getExtras());
        args.putInt(CoverImageFragment.BACKGROUND_RESOURCE, Android.R.color.black);
        final Fragment fragment = CoverImageFragment.newInstance(args);
        getSupportFragmentManager()
          .beginTransaction()
          .add(Android.R.id.content, fragment)
          .commit();
      }
    }
    
  • Il garde la manière de développer un Fragment proche de celui d'un Activity, c'est-à-dire Bundle en tant que "paramètres d'entrée, pas d'exceptions".

Quant aux inconvénients que vous avez mentionnés:

  • Je pense que la surcharge est minime parce que vous n'interrogerez probablement pas le Bundle dans une boucle serrée, donc obtenir vos données d'argument une fois dans onCreate(), onViewCreate() , etc. n'est pas si mal.

  • Pour la sécurité des types, Bundle a toutes les différentes méthodes getXXXX(), et même des surcharges pour fournir une valeur par défaut si quelque chose manque/facultatif :)

Quant aux méthodes newInstance(), je les considère comme un moyen facile d'encapsuler les appels new et setArguments() pour mes Fragment; Je fournis parfois une MyFragment newInstance(String singleIdOfWhatToDisplay) supplémentaire qui crée à la fois Bundle et Fragment en une seule fois et retourne une instance de Fragment prête à l'emploi.

48
Philipp Reichart

J'ai trouvé que c'était un problème TRÈS déroutant (l'un des nombreux qui jonchent le paysage Android)).

setArguments() est une solution de contournement pour le besoin très inutile d'Android d'avoir un constructeur sans paramètre disponible pour Fragments.

Ma confusion est venue par vagues. Tout d'abord, les méthodes que vous remplacez naturellement dans votre Fragment (par exemple onCreate, onCreateView) reçoivent un paramètre Bundle qui représente le savedInstanceState de votre Fragment. Cet état d'instance n'a apparemment RIEN que ce soit à voir avec les valeurs que vous stockez via setArguments() et récupérez via getArguments(). Les deux utilisent un Bundle, les deux Bundles sont susceptibles d'être accédés dans la même méthode redéfinie, et n'ont rien à voir l'un avec l'autre.

Deuxièmement, on ne sait pas comment Android utilise setArguments(). Android appelle votre constructeur sans paramètre pour reconstruire votre Fragment sur rotation, mais apparemment, AUSSI appellera la dernière méthode setArguments() appelée lors de la construction de Fragment.

Huh ????

Incroyable, mais vrai. Tout cela créant Bundles avec setArguments() folie existe pour compenser le besoin d'un constructeur Fragment sans paramètre.

En bref, j'utilise la méthode statique newInstance pour créer mon Fragment.

public MyFragment() {
    //satisfy Android
}

public static MyFragment newInstance(long record_id) {
    Log.d("MyFragment", "Putting " + record_id + " into newInstance");
    MyFragment f = new MyFragment();
    Bundle args = new Bundle();
    args.putLong("record_id", record_id);
    f.setArguments(args);
    return f;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    /**
     * Perform an immediate check of arguments,
     * which ARE NOT the same as the bundle used
     * for saved instance state.
     */
    Bundle args = getArguments();
    if(args != null) {
        record_id = args.getLong("record_id");
        Log.d("MyFragment", "found record_id of " + String.valueOf(record_id));
    }
    if(savedInstanceState != null) {
        //now do something with savedInstanceState
    }
}
19
rmirabelle

Je suis assez nouveau dans la programmation Android mais c'est ma compréhension actuelle du problème:

Le constructeur de Fragments ne peut pas avoir des paramètres. Lorsque votre activité est suspendue, votre fragment peut être libéré. Avant que votre activité ne reprenne, le système crée une nouvelle version de votre fragment appelant le constructeur. Si un constructeur autre que celui par défaut est utilisé, comment Android est-il censé savoir quels sont les types et les valeurs des arguments de votre constructeur Fragments?

Je ne pense pas que ce bundle soit sorti. Le bundle est conservé avec précision afin qu'il puisse être renvoyé à votre fragment après avoir été recréé avec le constructeur par défaut.

Philipp Reichart y a échappé dans son message (en fait plus qu'échappé.)

9
WayneJ

Je veux juste ajouter un inconvénient de plus aux arguments, c'est que vous devez créer dynamiquement des fragments. Comme arguments ne fonctionne pas très bien si vous créez à partir du xml. Et je déteste vraiment ça.

2
havexz