Quelle est la meilleure façon de partager des données entre les activités?

239

J'ai une activité qui est la principale activité utilisée dans l'application et qui comporte un certain nombre de variables. J'ai deux autres activités que j'aimerais pouvoir utiliser les données de la première activité. Maintenant je sais que je peux faire quelque chose comme ça:

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();

Cependant, je veux partager beaucoup de variables et certaines peuvent être plutôt grandes, donc je ne veux pas en créer des copies comme ci-dessus.

Existe-t-il un moyen d'obtenir et de modifier directement les variables sans utiliser les méthodes get et set? Je me souviens avoir lu un article sur le site de développement Google disant que ce n'est pas recommandé pour les performances sur Android.

Fou à lier
la source
2
Depuis Android 2.3 (Gingerbread), l'optimisation de get / set est effectuée automatiquement par Dalvik; cela n'est pertinent que si vous ciblez des versions plus anciennes d'Android.
StellarVortex
Notez que l'exemple ne copie pas les données de chaîne. Il crée plutôt une référence au même objet chaîne.
Code-Apprentice
C'est difficile à croire, pourquoi n'y a-t-il aucune possibilité de démarrer une activité à partir d'une autre activité et de passer un objet complexe du premier au deuxième? Sans sérialisation, sauvegarde de l'objet et tout cet effort. Est-ce une faille de sécurité ou quelle autre raison s'oppose à la simple transmission de la référence d'objet si les deux activités sont dans la même application? (Je comprends que c'est différent s'ils sont dans différentes applications)
Droidum
Veuillez consulter ce stackoverflow.com/questions/56521969/…
Levon Petrosyan
LiveData est la meilleure solution, la plus récente. Vérifiez ma réponse ci-dessous.
Amir Uval

Réponses:

476

Voici une compilation des moyens les plus courants pour y parvenir :

  • Envoyer des données à l'intérieur de l'intention
  • Champs statiques
  • HashMap of WeakReferences
  • Persister les objets (sqlite, partager les préférences, fichier, etc.)

TL; DR : il existe deux façons de partager des données: passer des données dans les extras de l'intention ou les enregistrer ailleurs. Si les données sont des primitives, des chaînes ou des objets définis par l'utilisateur: envoyez-les dans le cadre des extras d'intention (les objets définis par l'utilisateur doivent implémenter Parcelable). Si vous passez des objets complexes, enregistrez une instance dans un singleton ailleurs et accédez-y à partir de l'activité lancée.

Quelques exemples de comment et pourquoi mettre en œuvre chaque approche:

Envoyer des données dans des intentions

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);

Sur la deuxième activité:

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");

Utilisez cette méthode si vous transmettez des données primitives ou des chaînes . Vous pouvez également passer des objets qui implémentent Serializable.

Bien que tentant, vous devriez réfléchir à deux fois avant de l'utiliser Serializable: il est sujet aux erreurs et horriblement lent. Donc en général: éloignez-vousSerializable si possible. Si vous souhaitez transmettre des objets définis par l'utilisateur complexes, jetez un œil à l' Parcelableinterface . Il est plus difficile à mettre en œuvre, mais il a des gains de vitesse considérables par rapport à Serializable.

Partagez des données sans persister sur le disque

Il est possible de partager des données entre des activités en les enregistrant en mémoire étant donné que, dans la plupart des cas, les deux activités s'exécutent dans le même processus.

Remarque: parfois, lorsque l'utilisateur quitte votre activité (sans la quitter), Android peut décider de tuer votre application. Dans un tel scénario, j'ai rencontré des cas dans lesquels Android tente de lancer la dernière activité en utilisant l'intention fournie avant la mort de l'application. Dans ce cas, les données stockées dans un singleton (le vôtre ou Application) disparaîtront et de mauvaises choses pourraient se produire. Pour éviter de tels cas, vous pouvez soit persister les objets sur le disque, soit vérifier les données avant de les utiliser pour vous assurer qu'elles sont valides.

Utiliser une classe singleton

Demandez à une classe de conserver les données:

public class DataHolder {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}

  private static final DataHolder holder = new DataHolder();
  public static DataHolder getInstance() {return holder;}
}

De l'activité lancée:

String data = DataHolder.getInstance().getData();

Utiliser l'application singleton

Le singleton d'application est une instance android.app.Applicationqui est créée lorsque l'application est lancée. Vous pouvez en fournir un personnalisé en étendant Application:

import android.app.Application;
public class MyApplication extends Application {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}
}

Avant de lancer l'activité:

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);

Ensuite, à partir de l'activité lancée:

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();

Champs statiques

L'idée est fondamentalement la même que celle du singleton, mais dans ce cas, vous fournissez un accès statique aux données:

public class DataHolder {
  private static String data;
  public static String getData() {return data;}
  public static void setData(String data) {DataHolder.data = data;}
}

De l'activité lancée:

String data = DataHolder.getData();

HashMap of WeakReferences

Même idée, mais en permettant au garbage collector de supprimer les objets non référencés (par exemple lorsque l'utilisateur quitte l'activité):

public class DataHolder {
  Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();

  void save(String id, Object object) {
    data.put(id, new WeakReference<Object>(object));
  }

  Object retrieve(String id) {
    WeakReference<Object> objectWeakReference = data.get(id);
    return objectWeakReference.get();
  }
}

Avant de lancer l'activité:

DataHolder.getInstance().save(someId, someObject);

De l'activité lancée:

DataHolder.getInstance().retrieve(someId);

Vous devrez peut-être ou non passer l'ID d'objet à l'aide des extras de l'intention. Tout dépend de votre problème spécifique.

Persister les objets sur le disque

L'idée est de sauvegarder les données sur disque avant de lancer l'autre activité.

Avantages: vous pouvez lancer l'activité à partir d'autres endroits et, si les données sont déjà persistantes, cela devrait fonctionner correctement.

Inconvénients: il est lourd et prend plus de temps à mettre en œuvre. Nécessite plus de code et donc plus de chances d'introduire des bogues. Ce sera également beaucoup plus lent.

Certaines des façons de conserver les objets incluent:

Cristian
la source
11
Je dirais que ce n'est pas la voie "normale" pour des données plus grandes / plus complexes. Il est beaucoup plus facile d'utiliser un singleton statique ou l'objet Application, et cela fonctionne très bien. Maintenant que l'OP a utilisé une chaîne dans l'exemple, pour cela, l'intention est parfaite et préférée.
Charlie Collins
10
Serializable a révélé de sérieux problèmes de performances sur le modèle de processus Android. C'est pourquoi ils ont introduit Parcelable. Lisez Parcelable au lieu de Sérialisable dans la réponse ci-dessus.
Subin Sebastian
3
Cela se fait via la setResultméthode. De plus, dans ce cas, l'activité secondaire doit être appelée à l'aide de la startActivityForResultméthode.
Cristian
2
Excellent résumé! En ce qui concerne le problème des singletons détruits, il existe une solution simple pour les applications avec beaucoup d'activités et d'objets: utilisez une sous-classe de Activitypartout, et dans sa onCreate()vérification tout champ statique du singleton que vous remplissez lors du démarrage de l'application. Si ce champ est vide, revenez à l'activité de démarrage à l'aide de FLAG_ACTIVITY_CLEAR_TASKou de BroadcastReceiverpour supprimer d'autres activités.
Janosch
1
Je ne recommanderais pas d'enregistrer les données dans la classe d'application. En savoir plus ici: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan
22

Ce que vous pouvez utiliser:

  1. passer des données entre les activités (comme Cristian l'a dit)
  2. en utilisant une classe avec beaucoup de variables statiques (vous pouvez donc les appeler sans instance de la classe et sans utiliser getter / setter)
  3. Utiliser une base de données
  4. Préférences partagées

Ce que vous choisissez dépend de vos besoins. Vous utiliserez probablement plus d'une façon lorsque vous avez "beaucoup de"

WarrenFaith
la source
1
Veuillez noter que les statiques sont effacées à la mort du processus
EpicPandaForce
@EpicPandaForce bien sûr et aussi lorsque l'appareil a été éteint.
WarrenFaith
1
Mais si l'appareil est éteint, l'application redémarre à partir de l' MAINaction. Après la mort du processus, vous redémarrez sur toute activité ouverte en dernier, ce qui pourrait être une page de détails quelque part au fond de l'application.
EpicPandaForce
@EpicPandaForce semble que vous avez manqué mon ironie même si vous êtes Cpt Evident.
WarrenFaith
16

Faites ce que Google vous commande de faire! ici: http://developer.android.com/resources/faq/framework.html#3

  • Types de données primitifs
  • Objets non persistants
  • Classe Singleton - mon préféré: D
  • Un champ / méthode statique public
  • Un HashMap de références faibles aux objets
  • Objets persistants (préférences d'application, fichiers, fournisseurs de contenu, base de données SQLite)
Tylko Arka Gdynia
la source
1
Lien Google pour les objets persistants: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk
Seuls les modèles, la classe Singleton est le premier modèle de conception, une classe avec un champ / méthode statique se comporte beaucoup comme des singletons et créer une base de données pour des objets busniss persistants n'est parfois pas une bonne chose, bien sûr, ce n'est pas de votre faute et vous listez possible façons de l'atteindre, mais je me demande pourquoi Google a compliqué une chose aussi stupide, des problèmes de performances ou quoi ?? !!!! ou est-ce que je ne comprends pas la manière Android ?? !!!!
La VloZ Merrill
lien rompu :(
Shayan_Aryan
14

"Cependant, je veux partager beaucoup de variables et certaines peuvent être assez grandes, donc je ne veux pas en créer des copies comme ci-dessus."

Cela ne fait pas de copie (en particulier avec String , mais même les objets passent par la valeur de la référence, pas l'objet lui-même, et les getter comme ça sont bien à utiliser - sans doute mieux à utiliser que d'autres moyens car ils sont communs et bien compris). Les anciens «mythes sur les performances», comme le fait de ne pas utiliser les getters et les setters, ont toujours une certaine valeur, mais ont également été mis à jour dans les documents .

Mais si vous ne voulez pas faire cela, vous pouvez également simplement rendre les variables publiques ou protégées dans GlobalState et y accéder directement. Et, vous pouvez créer un singleton statique comme l' indique l'objet Application JavaDoc :

Il n'est normalement pas nécessaire de sous-classer Application. Dans la plupart des situations, les singletons statiques peuvent fournir les mêmes fonctionnalités de manière plus modulaire. Si votre singleton a besoin d'un contexte global (par exemple pour enregistrer des récepteurs de diffusion), la fonction pour le récupérer peut se voir attribuer un contexte qui utilise en interne Context.getApplicationContext () lors de la première construction du singleton.

L'utilisation des données d' intention , comme le notent d'autres réponses ici, est un autre moyen de transmettre des données, mais il est généralement utilisé pour des données plus petites et des types simples. Vous pouvez transmettre des données plus grandes / plus complexes, mais c'est plus compliqué que de simplement utiliser un singleon statique. L' objet Application est toujours mon préféré pour partager des données non persistantes plus grandes / plus complexes entre les composants d'application Android (car il a un cycle de vie bien défini dans une application Android).

En outre, comme d'autres l'ont noté, si les données deviennent très complexes et doivent être persistantes, vous pouvez également utiliser SQLite ou le système de fichiers.

Charlie Collins
la source
1
En fait, je suis récemment tombé sur ce sujet dans les documents: developer.android.com/guide/appendix/faq/framework.html#3 . Pour les "objets complexes non persistants", il recommande d'utiliser la classe Application pour créer et supprimer un singleton statique! De cette façon, vous obtenez l'application de cycle de vie bien définie et la facilité d'utilisation d'un singleton statique.
Charlie Collins
2
cette section de la FAQ semble être supprimée maintenant (je ne vois que des "objets non persistants" et aucune mention de la classe Application). Quoi qu'il en soit, vous pouvez élaborer?
Tony Chan
1
Il se trouve actuellement sur developer.android.com/guide/faq/framework.html#3 "Comment puis-je transmettre des données entre les activités / services au sein d'une seule application?", Et aucune mention de la classe Application.
Jerry101
J'aime utiliser un objet Application, en suivant le précédent que vous avez défini dans "Android en action". Mais beaucoup d'employeurs potentiels n'aiment pas cela lorsqu'ils voient cela dans les défis du code. Ils peuvent se tromper, mais ils jettent leur poids. BTW: ce lien 'framework.html # 3 ne fonctionne plus.
Matt
4

Il existe une nouvelle et meilleure façon de partager des données entre les activités, et c'est LiveData . Notez en particulier cette citation de la page du développeur Android:

Le fait que les objets LiveData prennent en compte le cycle de vie signifie que vous pouvez les partager entre plusieurs activités, fragments et services. Pour garder l'exemple simple, vous pouvez implémenter la classe LiveData en tant que singleton

L'implication de ceci est énorme - toutes les données de modèle peuvent être partagées dans une classe singleton commune à l'intérieur d'un LiveDatawrapper. Il peut être injecté des activités dans leurs domaines respectifs ViewModelpar souci de testabilité. Et vous n'avez plus à vous soucier des références faibles pour éviter les fuites de mémoire.

Amir Uval
la source
2

L'utilisation de la table de hachage de l'approche de référence faible, décrite ci-dessus, et dans http://developer.android.com/guide/faq/framework.html me semble problématique. Comment les entrées entières sont-elles récupérées, pas seulement la valeur de la carte? Dans quelle portée l'allouez-vous? Comme le cadre contrôle le cycle de vie de l'activité, le fait de posséder l'une des activités participantes risque de provoquer des erreurs d'exécution lorsque le propriétaire est détruit avant ses clients. Si l'application en est propriétaire, une activité doit supprimer explicitement l'entrée pour éviter que la table de hachage ne conserve les entrées avec une clé valide et une référence faible collectée potentiellement endommagée. De plus, que doit faire un client lorsque la valeur renvoyée pour une clé est nulle?

Il me semble qu'un WeakHashMap appartenant à l'application ou au sein d'un singleton est un meilleur choix. Une valeur dans la carte est accessible via un objet clé, et lorsqu'il n'existe aucune référence forte à la clé (c'est-à-dire que toutes les activités sont effectuées avec la clé et ce à quoi elle est mappée), GC peut récupérer l'entrée de la carte.

Mark Rosenberg
la source
2

Il existe différentes façons de partager des données entre les activités

1: Transmission de données entre les activités à l'aide d'Intention

Intent intent=new Intent(this, desirableActivity.class);
intent.putExtra("KEY", "Value");
startActivity(intent)

2: En utilisant un mot-clé statique, définissez la variable comme statique publique et utilisez n'importe où dans le projet

      public static int sInitialValue=0;

utiliser n'importe où dans le projet en utilisant classname.variableName;

3: Utilisation de la base de données

mais son processus est un peu long, vous devez utiliser une requête pour insérer des données et itérer les données en utilisant le curseur en cas de besoin. Mais il n'y a aucune chance de perdre des données sans nettoyer le cache.

4: Utilisation des préférences partagées

beaucoup plus facile que la base de données. mais il y a une limitation, vous ne pouvez pas enregistrer les objets ArrayList, List et custome.

5: Créez un setter getter dans la classe Aplication et accédez à n'importe où dans le projet.

      private String data;
      public String getData() {
          return data;
      }

      public void setData(String data) {
          this.data = data;
      }

ici mis et obtenir des activités

         ((YourApplicationClass)getApplicationContext()).setData("abc"); 

         String data=((YourApplicationClass)getApplicationContext()).getData();  
vikas singh
la source
1

Eh bien, j'ai quelques idées, mais je ne sais pas si elles sont ce que vous cherchez.

Vous pouvez utiliser un service contenant toutes les données, puis lier simplement vos activités au service pour la récupération des données.

Ou regroupez vos données dans un fichier sérialisable ou parcellable et attachez-les à un bundle et passez le bundle entre les activités.

Celui-ci n'est peut-être pas du tout ce que vous recherchez, mais vous pouvez également essayer d'utiliser des préférences partagées ou une préférence en général.

Quoi qu'il en soit, faites-moi savoir ce que vous décidez.

ahodder
la source
1

En supposant que vous appelez l'activité deux de l'activité un en utilisant une intention.
Vous pouvez transmettre les données avec l'intention.putExtra (),

Prenez ceci pour votre référence. Envoi de tableaux avec Intent.putExtra

J'espère que c'est ce que tu veux.

Manish Khot
la source
1

Si votre intention est d'appeler d'autres activités à partir de l'activité en cours, vous devez utiliser Intents . Votre objectif pourrait être moins de conserver des données que de les partager au besoin.

Cependant, si vous avez vraiment besoin de conserver ces valeurs, vous pouvez les conserver dans une sorte de fichier texte structuré ou de base de données sur le stockage local. Un fichier de propriétés, un fichier XML ou un fichier JSON peut stocker vos données et être facilement analysé lors de la création d'activité. N'oubliez pas également que vous avez SQLite sur tous les appareils Android, vous pouvez donc les stocker dans une table de base de données. Vous pouvez également utiliser une carte pour stocker des paires clé-valeur et sérialiser la carte vers le stockage local, mais cela peut être trop lourd pour être utile pour des structures de données simples.

Mike Yockey
la source
1

Toutes les réponses susmentionnées sont excellentes ... J'ajoute juste une que personne n'avait encore mentionnée sur la persistance des données à travers les activités et qui consiste à utiliser la base de données SQLite Android intégrée pour conserver les données pertinentes ... En fait, vous pouvez placer votre databaseHelper dans l'état de l'application et appelez-le selon les besoins tout au long des activations .. Ou créez simplement une classe d'assistance et effectuez les appels de base de données en cas de besoin ... Il suffit d'ajouter une autre couche à prendre en compte ... Mais toutes les autres réponses suffiraient aussi bien. Vraiment juste une préférence

avatar
la source
1

Partage de données entre activités exemple passant un email après la connexion

"email" est le nom qui peut être utilisé pour référencer la valeur de l'activité demandée

1 Code sur la page de connexion

Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
    openLoginActivity.putExtra("email", getEmail);

2 code sur la page d'accueil

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");
Jeff
la source
1

Et si vous voulez travailler avec un objet de données, ces deux implémentations sont très importantes:

Sérialisable vs Parcelable

  • Sérialisable est une interface de marqueur, ce qui implique que l'utilisateur ne peut pas rassembler les données en fonction de ses besoins. Ainsi, lorsque l'objet implémente Java sérialisable, il le sérialise automatiquement.
  • Parcelable est le propre protocole de sérialisation d'Android. Dans Parcelable, les développeurs écrivent du code personnalisé pour le marshaling et le démarshaling. Il crée donc moins d'objets poubelles par rapport à la sérialisation
  • Les performances de Parcelable sont très élevées par rapport à Serializable en raison de son implémentation personnalisée. Il est fortement recommandé d'utiliser l'implantation de Parcelable lors de la sérialisation d'objets dans Android.

public class User implements Parcelable

vérifiez plus ici

Diego Venâncio
la source