Come inviare oggetti tramite bundle


119

Devo passare un riferimento alla classe che esegue la maggior parte della mia elaborazione tramite un bundle.

Il problema è che non ha nulla a che fare con intenti o contesti e ha una grande quantità di oggetti non primitivi. Come impacchettare la classe in un parcelable / serializable e passarla a un startActivityForResult?


2
"Ho bisogno di passare un riferimento alla classe che esegue la maggior parte della mia elaborazione tramite un bundle" - perché?
CommonsWare

1
Ho un oggetto (DataManager), gestisce un server ed esegue alcuni backend per alcune GUI. Quando viene stabilita una nuova connessione, desidero che l'utente sia in grado di avviare una nuova attività che elenca in ListView tutte le connessioni attive e che l'utente ne scelga una. I dati risultanti verranno quindi collegati a una nuova GUI. Questo è davvero solo uno skin choser per il back-end.
ahodder

3
Se hai a che fare con la stessa istanza di un oggetto su più attività, potresti prendere in considerazione il pattern singleton . C'è un buon tutorial qui .
sotrh

Risposte:


55

Capire quale strada prendere richiede di rispondere non solo alla domanda chiave di CommonsWare sul "perché", ma anche alla domanda "a cosa?" lo stai passando.

La realtà è che l'unica cosa che può passare attraverso i pacchetti sono i dati semplici: tutto il resto si basa sulle interpretazioni di ciò che i dati significano o indicano. Non puoi letteralmente passare un oggetto, ma quello che puoi fare è una delle tre cose:

1) È possibile suddividere l'oggetto fino ai suoi dati costitutivi e se ciò che dall'altra parte ha la conoscenza dello stesso tipo di oggetto, può assemblare un clone dai dati serializzati. È così che la maggior parte dei tipi comuni passa attraverso i pacchetti.

2) Puoi passare una maniglia opaca. Se lo stai passando nello stesso contesto (anche se ci si potrebbe chiedere perché preoccuparsi) quello sarà un handle che puoi invocare o dereferenziare. Ma se lo passi attraverso Binder in un contesto diverso, il suo valore letterale sarà un numero arbitrario (infatti, questi numeri arbitrari contano sequenzialmente dall'inizio). Non puoi fare altro che tenerne traccia, finché non lo ritrasmetti al contesto originale che farà sì che Binder lo trasformi di nuovo nella maniglia originale, rendendolo nuovamente utile.

3) Puoi passare una maniglia magica, come un descrittore di file o un riferimento a determinati oggetti del sistema operativo / piattaforma, e se imposti i flag corretti Binder creerà un clone che punta alla stessa risorsa per il destinatario, che può essere effettivamente utilizzato su l'altra estremità. Ma questo funziona solo per pochissimi tipi di oggetti.

Molto probabilmente, stai passando la tua classe solo in modo che l'altra estremità possa tenerne traccia e restituirla in un secondo momento, oppure la stai passando a un contesto in cui è possibile creare un clone da dati costituenti serializzati ... oppure stai cercando di fare qualcosa che semplicemente non funzionerà e devi ripensare l'intero approccio.


1
Grazie per la risposta del tour. Hai ragione, tutto quello che devo fare è passare un riferimento di un elenco di oggetti alla mia nuova attività. La nuova attività prenderà alcuni dati dall'elenco e visualizzerà un ListView selezionabile. onSelect, l'attività restituirà un risultato (alcuni dati relativi all'oggetto clic) all'attività host. Se ho capito bene, credo che la tua opzione 2 lo gestisca in modo più appropriato; come ottengo questa maniglia opaca?
ahodder

L'altra tua attività non può estrarre dati da un oggetto opaco da visualizzare. Quello che probabilmente vuoi fare è creare e trasmettere alcuni oggetti surrogati di un tipo supportato che contengono copie delle informazioni che verrebbero visualizzate.
Chris Stratton

158

Puoi anche usare Gson per convertire un oggetto in un JSONObject e passarlo in bundle. Per me è stato il modo più elegante che ho trovato per farlo. Non ho testato come influisce sulle prestazioni.

Nell'attività iniziale

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

Nella prossima attività

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

3
Poiché si tratta di passare cose tra le attività, non accade abbastanza spesso da avere un grande impatto sulle prestazioni generali dell'app. Detto questo, dubito che funzionerebbe per serializzare il DataManager del post originale poiché sembra che abbia connessioni socket e altre classi simili.
britzl

4
Anche Google suggerisce questa soluzione invece di Serialize: controlla l'ultima sezione "Alternative consigliate" di questa pagina del documento
TechNyquist

3
solo come avvertimento, ho seguito questa tecnica per un po 'ma c'è un limite di memoria a ciò che puoi passare come stringa, quindi assicurati che i tuoi dati non siano troppo grandi.
jiduvah

Vedi blog.madadipouya.com/2015/09/21/… per sapere come aggiungere il supporto Gson al tuo progetto.
geekQ

Soluzione intelligente, tanto di cappello!
Rohit Mandiwal

20

L' interfaccia Parcelable è un buon modo per passare un oggetto con un Intent.

Come posso rendere parcellizzabili i miei oggetti personalizzati? è una risposta abbastanza buona su come utilizzare Parcelable

I documenti ufficiali di Google includono anche un esempio


1
oppure possono anche essere serializzabili.
Jeffrey Blattman

1
Ma riduce notevolmente le prestazioni di 10 volte !!
Dai

2
+1 @ Il commento di Mati, tuttavia, per metterlo nel contesto 10x quando applicato a un singolo oggetto è l'equivalente di 1 ms. Quindi forse non è così male come sembra.
pinoyyid

1
Essere d'accordo. Il problema è quando gestisci le collezioni, che è un caso d'uso molto comune se stai ottenendo risorse da un'API Rest. Ma per un singolo oggetto, non dovrebbe essere qualcosa di noto. Ad ogni modo, se tutto il codice boilerplate è qualcosa che ti ostacola, puoi provare questa libreria che genera tutto per te: github.com/johncarl81/parceler . Davvero un bell'approccio!
saiyancoder

Link interrotto: 404 (non trovato)
Gallal

14

È possibile utilizzare lo stato dell'applicazione globale .

Aggiornare:

Personalizza e poi aggiungilo al tuo AndroidManifest.xml:

<application android:label="@string/app_name" android:debuggable="true" android:name=".CustomApplication"

E poi fai una lezione nel tuo progetto come questa:

package com.example;

import android.app.Application;

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

E poiché " È possibile accedervi tramite getApplication () da qualsiasi attività o servizio ", lo usi in questo modo:

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

Spero che aiuti.


1
Grazie per la risposta, ma come?
ahodder

Credo che tu sia solo sottoclasse Applicazione e puoi quindi memorizzare tutto ciò che ti piace. Le modifiche xml necessarie sono menzionate nel collegamento sopra.
Mark Storer

9
In qualità di responsabile della progettazione generale, è una buona idea evitare le globali a meno che non ne abbiate davvero bisogno. In questo caso ci sono buone alternative.
dhaag23

Ok, penso di aver capito quello che stai dicendo. Basta estendere l'applicazione e inserire una variabile contenente l'oggetto che deve essere passato; Ho esaminato la pagina di riferimento e non ho visto le modifiche xml necessarie.
2010

Volevo scrivere anche questo come risposta. Questo è sicuramente uno dei modi per farlo. Ma tieni presente che questi oggetti rimangono nella memoria a meno che non li decifri (o il contesto dell'app viene distrutto) e possono occupare spazio quando non ne hai bisogno.
Igor Čordaš


10

Possibile soluzione:

Bundle bundle = new Bundle();
bundle.putSerializable("key", new CustomObject());

Oggetto personalizzato di classe:

class CustomObject implements Serializable{
 private SubCustomObject1 sc1;
 private SubCustomObject2 sc2;
}

Oggetti subcustom:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

Un altro modo per inviare oggetti tramite bundle consiste nell'usare il bundle.putByteArray
codice di esempio

public class DataBean implements Serializable {
private Date currentTime;

public setDate() {
    currentTime = Calendar.getInstance().getTime();
 }

public Date getCurrentTime() {
    return currentTime;
 }
}

inserire Object of DataBean in Bundle:

class FirstClass{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//When you want to start new Activity...
Intent dataIntent =new Intent(FirstClass.this, SecondClass.class);
            Bundle dataBundle=new Bundle();
            DataBean dataObj=new DataBean();
            dataObj.setDate();
            try {
                dataBundle.putByteArray("Obj_byte_array", object2Bytes(dataObj));

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }

            dataIntent.putExtras(dataBundle);

            startActivity(dataIntent);
}

Conversione di oggetti in array di byte

/**
 * Converting objects to byte arrays
 */
static public byte[] object2Bytes( Object o ) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream( baos );
      oos.writeObject( o );
      return baos.toByteArray();
    }

Ottieni indietro l'oggetto dal pacchetto:

class SecondClass{
DataBean dataBean;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//Get Info from Bundle...
    Bundle infoBundle=getIntent().getExtras();
    try {
        dataBean = (DataBean)bytes2Object(infoBundle.getByteArray("Obj_byte_array"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Metodo per ottenere oggetti da array di byte:

/**
 * Converting byte arrays to objects
 */
static public Object bytes2Object( byte raw[] )
        throws IOException, ClassNotFoundException {
      ByteArrayInputStream bais = new ByteArrayInputStream( raw );
      ObjectInputStream ois = new ObjectInputStream( bais );
      Object o = ois.readObject();
      return o;
    }

Spero che questo possa aiutare altri amici.


questo sembra liscio e facile guardando il codice. Ma credo che ci sia qualcosa di più sul motivo per cui l'SDK non offre qualcosa di simile per passare oggetti. Puoi dirmi qualcosa di più su questa soluzione?
Mario Lenci

3
Non c'è affatto bisogno di tutto quel codice! Usa bundle.putSerializable (objectImplementingSerializable) - questo fa sotto quello che stai reimplementando di nuovo qui ...
Risadinha

3

1.Un esempio molto diretto e facile da usare, rendere l'oggetto da passare implementare Serializable.

class Object implements Serializable{
    String firstName;
   String lastName;
}

2.passare l'oggetto in bundle

Bundle bundle = new Bundle();
Object Object = new Object();
bundle.putSerializable("object", object);

3.Ricevi l'oggetto passato dal bundle come Serializable quindi cast a Object.

Object object = (Object) getArguments().getSerializable("object");

0

Questa è una risposta molto tardiva alla mia domanda, ma continua a ricevere attenzione, quindi sento di doverla affrontare. La maggior parte di queste risposte sono corrette e gestiscono perfettamente il lavoro. Tuttavia, dipende dalle esigenze dell'applicazione. Questa risposta verrà utilizzata per descrivere due soluzioni a questo problema.

Applicazione

La prima è l' applicazione , poiché qui si è parlato di più. L'applicazione è un buon oggetto per posizionare entità che necessitano di un riferimento a un contesto. Un `ServerSocket` senza dubbio avrebbe bisogno di un contesto (per I / o file o semplici aggiornamenti di` ListAdapter`). Personalmente preferisco questa strada. Mi piacciono le applicazioni, sono utili per il recupero del contesto (perché possono essere rese statiche e probabilmente non causano una perdita di memoria) e hanno un ciclo di vita semplice.

Servizio

Il servizio` è secondo. Un "servizio" è in realtà la scelta migliore per il mio problema perché è ciò per cui i servizi sono progettati:
Un servizio è un componente dell'applicazione che può eseguire operazioni di lunga durata in
lo sfondo e non fornisce un'interfaccia utente.
I servizi sono puliti in quanto hanno un ciclo di vita più definito e più facile da controllare. Inoltre, se necessario, i servizi possono essere eseguiti esternamente all'applicazione (ad esempio all'avvio). Questo può essere necessario per alcune app o solo per una bella funzionalità.

Anche questa non era una descrizione completa, ma ho lasciato i collegamenti ai documenti per coloro che vogliono approfondire. Nel complesso, Serviceè meglio per l'istanza di cui avevo bisogno: eseguire un ServerSocket sul mio dispositivo SPP.


0

Mi sono imbattuto in questa domanda quando stavo cercando un modo per passare un oggetto Date. Nel mio caso, come suggerito tra le risposte, ho usato Bundle.putSerializable () ma non funzionerebbe per una cosa complessa come il DataManager descritto nel post originale.

Il mio suggerimento che darà un risultato molto simile all'inserimento di detto DataManager nell'applicazione o renderlo un Singleton è di usare Dependency Injection e associare DataManager a un ambito Singleton e iniettare DataManager ovunque sia necessario. Non solo si ottiene il vantaggio di una maggiore testabilità, ma si ottiene anche un codice più pulito senza tutto il codice "che passa le dipendenze tra classi e attività". (Robo) Guice è molto facile da lavorare e anche il nuovo framework Dagger sembra promettente.


1
Bene, con qualcosa come una data, potresti semplicemente passare il valore lungo. Ma il resto suona bene. Grazie.
partenza

0

un altro modo semplice per passare un oggetto utilizzando un bundle:

  • nell'oggetto classe, creare un elenco statico o un'altra struttura dati con una chiave
  • quando crei l'oggetto, inseriscilo nella struttura lista / dati con la chiave (es. il timestamp lungo quando l'oggetto viene creato)
  • creare il metodo static getObject (tasto lungo) per ottenere l'oggetto dalla lista
  • nel bundle passare la chiave, in modo da poter ottenere l'oggetto in un secondo momento da un altro punto del codice
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.