121 votes

Comment envoyer des objets à travers un bundle

Je dois passer une référence à la classe qui effectue la majorité de mon traitement à travers un bundle.

Le problème est qu'il n'a rien à voir avec des intents ou des contextes et contient une grande quantité d'objets non primitifs. Comment puis-je empaqueter la classe dans un parcelable/sérializable et la passer à une startActivityForResult?

2 votes

Je dois transmettre une référence à la classe qui effectue la majorité de mon traitement à travers un bundle - Pourquoi ?

1 votes

J'ai un objet (DataManager), qui gère un serveur et exécute quelques back-ends pour certaines interfaces graphiques. Chaque fois qu'une nouvelle connexion est établie, je veux que l'utilisateur puisse démarrer une nouvelle activité qui répertorie dans ListView toutes les connexions actives et permet à l'utilisateur d'en choisir une. Les données résultantes seront ensuite liées à une nouvelle interface graphique. C'est vraiment juste un sélecteur d'apparence pour l'arrière-plan.

3 votes

Si vous travaillez avec la même instance d'un objet sur plusieurs activités, vous voudrez peut-être envisager le patron singleton. Il y a un bon tutoriel ici.

159voto

Laranjeiro Points 667

Vous pouvez également utiliser Gson pour convertir un objet en JSONObject et le passer sur un bundle. Pour moi, c'était la manière la plus élégante que j'ai trouvée pour le faire. Je n'ai pas testé comment cela affecte les performances.

Dans l'activité initiale

Intent activity = new Intent(MyActivity.this, NextActivity.class);
activity.putExtra("myObject", new Gson().toJson(myobject));
startActivity(activity);

Dans l'activité suivante

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);

3 votes

Depuis qu'il s'agit de passer des éléments entre les activités, cela n'arrive pas assez souvent pour avoir un grand impact sur les performances globales de l'application. Cela dit, je doute que cela fonctionnerait de sérialiser le DataManager du message original car il semble qu'il a des connexions de socket et d'autres classes similaires.

4 votes

Aussi, Google suggère cette solution au lieu de Serialize: consultez la dernière section "Alternatives recommandées" de cette page de documentation

3 votes

Juste comme un avertissement, j'ai suivi cette technique pendant un certain temps mais il y a une limite de mémoire à ce que vous pouvez passer en tant que chaîne, donc assurez-vous que vos données ne sont pas trop volumineuses.

56voto

Chris Stratton Points 23061

Comprendre quel chemin prendre nécessite de répondre non seulement à la question clé de "pourquoi" de CommonsWare, mais aussi à la question de "à quoi?" vous le passez.

La réalité est que la seule chose qui peut passer à travers les bundles est des données brutes - tout le reste est basé sur des interprétations de ce que ces données signifient ou pointent. Vous ne pouvez pas littéralement passer un objet, mais ce que vous pouvez faire est l'une des trois choses suivantes :

1) Vous pouvez décomposer l'objet en ses données constitutives, et si ce qui se trouve de l'autre côté a connaissance du même type d'objet, il peut assembler un clone à partir des données sérialisées. C'est ainsi que la plupart des types communs passent à travers les bundles.

2) Vous pouvez passer une poignée opaque. Si vous le passez dans le même contexte (bien que l'on puisse se demander pourquoi s'embêter) ce sera une poignée que vous pouvez invoquer ou déréférencer. Mais si vous le passez au travers de Binder vers un contexte différent, sa valeur réelle sera un nombre arbitraire (en fait, ces nombres arbitraires sont numérotés séquentiellement depuis le démarrage). Vous ne pouvez rien faire d'autre que le suivre, jusqu'à ce que vous le renvoyiez au contexte d'origine qui permettra à Binder de le transformer à nouveau en la poignée d'origine, la rendant ainsi à nouveau utile.

3) Vous pouvez passer une poignée magique, comme un descripteur de fichier ou une référence à certains objets du système d'exploitation, et si vous définissez les bons indicateurs Binder créera un clone pointant vers la même ressource pour le destinataire, qui pourra réellement l'utiliser de l'autre côté. Mais cela ne fonctionne que pour quelques types d'objets.

Le plus probable est que vous passez votre classe juste pour que l'autre côté puisse le suivre et vous le rendre plus tard, ou que vous le passez à un contexte où un clone peut être créé à partir de données constitutives sérialisées... sinon vous essayez de faire quelque chose qui ne va tout simplement pas fonctionner et vous devez repenser toute l'approche.

1 votes

Merci pour votre réponse. Vous avez raison, tout ce que j'ai besoin de faire est de passer une référence d'une liste d'objets à ma nouvelle activité. La nouvelle activité prendra des données de la liste et affichera un ListView sélectionnable. Lors de la sélection, l'activité renverra un résultat (des données concernant l'objet cliqué) à l'activité hôte. Si je comprends bien, je crois que votre deuxième option gère cela de la manière la plus appropriée; comment obtenir cette poignée opaque ?

0 votes

Votre autre activité ne peut pas extraire de données d'un objet opaque à afficher. Ce que vous voulez probablement faire est de créer et de passer des objets de substitution d'un type pris en charge qui contiennent des copies des informations qui seraient affichées.

22voto

chris Points 370

La interface Parcelable est une bonne façon de passer un objet avec un Intent.

Comment puis-je rendre mes objets personnalisés Parcelable? est une réponse assez bonne sur la façon d'utiliser Parcelable

Les google docs officiels incluent également un exemple

2 votes

Ou ils peuvent également être sérialisables.

1 votes

Mais réduit considérablement les performances de 10 fois !! Consultez ce benchmark : developerphil.com/parcelable-vs-serializable

2 votes

+1 au commentaire de @Mati, cependant pour le remettre en contexte 10x lorsqu'il est appliqué à un seul objet équivaut à 1 ms. Donc peut-être pas aussi grave que ça en a l'air.

14voto

Neil D Points 1321

Vous pourriez utiliser l'état global de l'application.

Mise à jour :

Personnalisez puis ajoutez ceci à votre AndroidManifest.xml :

`

Ensuite, créez une classe dans votre projet comme ceci :

package com.example;

import android.app.Application;

public class CustomApplication extends Application {
    public int someVariable = -1;
}

Et parce que "Il peut être accédé via getApplication() depuis n'importe quelle Activité ou Service", vous l'utilisez comme ceci :

CustomApplication application = (CustomApplication)getApplication();
application.someVariable = 123; 

J'espère que cela vous aide.

`

1 votes

Merci pour la réponse, mais comment?

0 votes

Je crois que vous devez simplement sous-classer Application et pouvez ensuite stocker ce que vous voulez. Les changements xml nécessaires sont mentionnés dans le lien ci-dessus.

9 votes

En tant que principe général de conception, il est préférable d'éviter les variables globales sauf si vous en avez vraiment besoin. Dans ce cas, il existe de bonnes alternatives.

13voto

dhaag23 Points 3709

Vous pouvez également rendre vos objets Sérializables et utiliser les méthodes getSerializable et putSerializable de Bundle.

1 votes

J'ai essayé cela et j'ai rapidement réalisé que ce serait peu pratique. Je ne pense pas que la plupart des objets stockés dans la classe passée (threads) soient sérialisables. :) merci quand même.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X