Qual è il modo migliore per condividere i dati tra le attività?


239

Ho un'attività che è l'attività principale utilizzata in tutta l'app e ha un numero di variabili. Ho altre due attività che vorrei poter utilizzare i dati della prima attività. Ora so di poter fare qualcosa del genere:

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

Tuttavia, voglio condividere molte variabili e alcune potrebbero essere piuttosto grandi, quindi non voglio crearne copie come sopra.

C'è un modo per ottenere e modificare direttamente le variabili senza usare i metodi get e set? Ricordo di aver letto un articolo sul sito di sviluppo di Google dicendo che questo non è raccomandato per le prestazioni su Android.


2
A partire da Android 2.3 (Gingerbread), l'ottimizzazione di get / set viene eseguita automaticamente da Dalvik; questo è rilevante solo se si sta prendendo di mira le versioni precedenti di Android.
StellarVortex,

Si noti che l'esempio non copia i dati della stringa. Piuttosto crea un riferimento allo stesso oggetto stringa.
Apprendista di Codice,

È difficile da credere, perché non c'è possibilità di iniziare un'attività da un'altra attività e passare qualsiasi oggetto complesso dal primo al secondo? Senza serializzazione, salvando l'oggetto e tutto quello sforzo. È una falla di sicurezza o quale altra ragione è contro il semplice passaggio del riferimento all'oggetto se entrambe le attività si trovano nella stessa app? (Capisco che è diverso se si trovano in app diverse)
Droidum,


LiveData è la soluzione migliore e più recente. Controlla la mia risposta qui sotto.
Amir Uval,

Risposte:


476

Ecco una raccolta dei modi più comuni per raggiungere questo obiettivo :

  • Invia dati con intento
  • Campi statici
  • HashMap di WeakReferences
  • Oggetti persistenti (sqlite, preferenze di condivisione, file, ecc.)

TL; DR : ci sono due modi per condividere i dati: passare i dati negli extra dell'intento o salvarli da qualche altra parte. Se i dati sono primitivi, stringhe o oggetti definiti dall'utente: inviarli come parte degli extra di intento (gli oggetti definiti dall'utente devono implementare Parcelable). Se si passano oggetti complessi, salvare un'istanza in un singleton da qualche altra parte e accedervi dall'attività avviata.

Alcuni esempi di come e perché implementare ciascun approccio:

Invia dati con intenti

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

Sulla seconda attività:

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

Utilizzare questo metodo se si stanno passando dati o stringhe primitivi . Puoi anche passare oggetti che implementano Serializable.

Anche se allettante, dovresti pensarci due volte prima dell'uso Serializable: è soggetto a errori e orribilmente lento. Quindi in generale: stai lontano daSerializable se possibile. Se vuoi passare oggetti complessi definiti dall'utente, dai un'occhiata Parcelableall'interfaccia . È più difficile da implementare, ma ha notevoli guadagni di velocità rispetto a Serializable.

Condividi i dati senza persistere sul disco

È possibile condividere i dati tra le attività salvandole in memoria dato che, nella maggior parte dei casi, entrambe le attività vengono eseguite nello stesso processo.

Nota: a volte, quando l'utente lascia la tua attività (senza chiuderla), Android può decidere di terminare l'applicazione. In tale scenario, ho riscontrato casi in cui Android tenta di avviare l'ultima attività utilizzando l'intento fornito prima che l'app venisse uccisa. In questi casi, i dati memorizzati in un singleton (o tuo o Application) spariranno e potrebbero accadere cose brutte. Per evitare tali casi, è necessario persistere oggetti sul disco o controllare i dati prima di utilizzarli per assicurarsi che siano validi.

Usa una classe singleton

Avere una classe per contenere i dati:

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;}
}

Dall'attività avviata:

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

Usa l'applicazione singleton

L'applicazione singleton è un'istanza di android.app.Applicationcui viene creata all'avvio dell'app. Puoi fornirne uno personalizzato estendendo 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;}
}

Prima di avviare l'attività:

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

Quindi, dall'attività avviata:

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

Campi statici

L'idea è sostanzialmente la stessa del singleton, ma in questo caso fornisci accesso statico ai dati:

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

Dall'attività avviata:

String data = DataHolder.getData();

HashMap di WeakReferences

Stessa idea, ma consentendo al garbage collector di rimuovere oggetti senza riferimento (ad es. Quando l'utente abbandona l'attività):

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();
  }
}

Prima di avviare l'attività:

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

Dall'attività avviata:

DataHolder.getInstance().retrieve(someId);

Potrebbe essere necessario o meno passare l'ID oggetto utilizzando gli extra dell'intento. Tutto dipende dal tuo problema specifico.

Persistere gli oggetti sul disco

L'idea è di salvare i dati sul disco prima di avviare l'altra attività.

Vantaggi: è possibile avviare l'attività da altri luoghi e, se i dati sono già persistiti, dovrebbe funzionare bene.

Svantaggi: è ingombrante e richiede più tempo per essere implementato. Richiede più codice e quindi maggiori possibilità di introdurre bug. Sarà anche molto più lento.

Alcuni dei modi per mantenere gli oggetti includono:


11
Direi che questo non è il modo "normale" per dati più grandi / più complessi. È molto più facile usare un singleton statico o l'oggetto Application, e funziona benissimo. Ora, detto questo, l'esempio ha usato una stringa nell'esempio, per questo l'intento è perfetto e preferito.
Charlie Collins,

10
Serializable ha riscontrato seri problemi di prestazioni sul modello di processo Android. Ecco perché hanno introdotto Parcelable. Leggi Parcelable anziché Serializable nella risposta sopra.
Subin Sebastian

3
Questo viene fatto tramite il setResultmetodo. Inoltre, in tal caso, l'attività secondaria deve essere invocata utilizzando il startActivityForResultmetodo.
Cristian,

2
Ottimo riassunto! Per quanto riguarda il problema della distruzione dei singleton, esiste una soluzione semplice per le app con molte attività e oggetti: usa una sottoclasse di in Activitytutto e onCreate()controlla ogni campo statico del singleton che riempi all'avvio dell'app. Se quel campo è vuoto, torna all'attività iniziale usando FLAG_ACTIVITY_CLEAR_TASKo a BroadcastReceiverper uccidere altre attività.
Janosch,

1
Non consiglierei di salvare i dati nella classe di applicazione. Per saperne di più qui: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

Cosa puoi usare:

  1. passare i dati tra le attività (come ha detto Cristian)
  2. usando una classe con molte variabili statiche (così puoi chiamarle senza un'istanza della classe e senza usare getter / setter)
  3. Utilizzando un database
  4. Preferenze condivise

Quello che scegli dipende dalle tue esigenze. Probabilmente userai più di un modo quando hai "un sacco di"


1
Si noti che le statistiche vengono cancellate in caso di morte del processo
EpicPandaForce,

@EpicPandaForce ovviamente e anche quando il dispositivo è stato spento.
WarrenFaith,

1
Ma se il dispositivo è spento, l'app si riavvia MAINdall'azione. Dopo la morte del processo, riavvii su qualsiasi attività aperta per ultima, che potrebbe essere una pagina di dettaglio da qualche parte nel profondo dell'app.
EpicPandaForce,

@EpicPandaForce sembra che ti sia persa la mia ironia anche se sei Cpt Ovvio.
WarrenFaith,

16

Fai ciò che Google ti ordina di fare! qui: http://developer.android.com/resources/faq/framework.html#3

  • Tipi di dati primitivi
  • Oggetti non persistenti
  • Classe Singleton - la mia preferita: D
  • Un campo / metodo statico pubblico
  • Una mappa hash di riferimenti deboli agli oggetti
  • Oggetti persistenti (Preferenze applicazione, File, contentProviders, SQLite DB)


Solo antipattern, la classe Singleton è il primo modello di progettazione, una classe con campo / metodo statici si comporta in modo molto simile ai singleton e creare database per oggetti busniss persistenti a volte non è una buona cosa, ovviamente non è colpa tua e stai elencando possibili modi per ottenerlo, ma mi chiedo perché Google abbia complicato una cosa così stupida, problemi di prestazioni o cosa ?? !!!! o non capisco il modo Android ?? !!!!
La VloZ Merrill,

il collegamento è interrotto :(
Shayan_Aryan il

14

"Tuttavia, voglio condividere molte variabili e alcune potrebbero essere piuttosto grandi, quindi non voglio crearne copie come sopra."

Ciò non crea una copia (specialmente con String , ma anche gli oggetti vengono passati in base al valore del riferimento, non all'oggetto stesso, e il getter è così che va bene usare - probabilmente meglio da usare rispetto ad altri mezzi perché sono comuni e capito bene). I "miti delle prestazioni" più vecchi, come non usare getter e setter, hanno ancora un certo valore, ma sono stati anche aggiornati nei documenti .

Ma se non vuoi farlo, puoi anche rendere pubbliche o protette le variabili in GlobalState e accedervi direttamente. E, puoi creare un singleton statico come indica l' oggetto Application JavaDoc :

Normalmente non è necessario sottoclassare l'Applicazione. Nella maggior parte dei casi, i singoli statici possono fornire la stessa funzionalità in un modo più modulare. Se il tuo singleton necessita di un contesto globale (ad esempio per registrare i ricevitori di trasmissione), alla funzione per recuperarlo può essere assegnato un contesto che utilizza internamente Context.getApplicationContext () quando costruisce il singleton per la prima volta.

L'uso dei dati di Intent , come altre note qui riportate è un altro modo per passare i dati, ma di solito viene utilizzato per dati più piccoli e tipi semplici. Puoi trasmettere dati più grandi / più complessi, ma è più coinvolto del semplice utilizzo di un singolo statico. L' oggetto Application è ancora il mio preferito per la condivisione di dati non persistenti più grandi / più complessi tra i componenti dell'applicazione Android (perché ha un ciclo di vita ben definito in un'app Android).

Inoltre, come altri hanno notato, se i dati diventano molto complessi e devono essere persistenti, è possibile utilizzare anche SQLite o il filesystem.


1
In realtà, mi sono appena imbattuto in questo nei documenti di recente: developer.android.com/guide/appendix/faq/framework.html#3 . Per "oggetti complessi non persistenti" si consiglia di utilizzare la classe Application per creare e abbattere un singleton statico! In questo modo si ottiene il ciclo di vita ben definito fornito dall'applicazione e la facilità d'uso di un singleton statico.
Charlie Collins,

2
quella sezione della faq sembra essere rimossa ora (vedo solo "oggetti non persistenti" e nessuna menzione della classe Application). Comunque puoi elaborare?
Tony Chan,

1
È attualmente su developer.android.com/guide/faq/framework.html#3 "Come posso passare i dati tra attività / servizi all'interno di una singola applicazione?" E nessuna menzione della classe Application.
Jerry101,

Mi piace usare un oggetto Applicazione, seguendo il precedente impostato in "Android in Action". Ma a molti potenziali datori di lavoro non piace quando lo vedono nelle sfide del codice. Potrebbero sbagliarsi, ma gettano il loro peso. A proposito: quel link 'framework.html # 3 non funziona più.
Matt J.


4

Esiste un modo nuovo e migliore per condividere i dati tra attività ed è LiveData . Nota in particolare questa citazione dalla pagina dello sviluppatore Android:

Il fatto che gli oggetti LiveData siano consapevoli del ciclo di vita significa che è possibile condividerli tra più attività, frammenti e servizi. Per rendere semplice l'esempio, è possibile implementare la classe LiveData come singleton

Le implicazioni sono enormi: qualsiasi dato di modello può essere condiviso in una classe singleton comune all'interno di un LiveDatawrapper. Può essere iniettato dalle attività nei rispettivi ViewModelper motivi di testabilità. E non è più necessario preoccuparsi di riferimenti deboli per evitare perdite di memoria.


2

L'uso dell'hashmap di un approccio di riferimento debole, sopra descritto e in http://developer.android.com/guide/faq/framework.html mi sembra problematico. Come vengono recuperate intere voci, non solo il valore della mappa? In quale ambito lo allocare? Poiché il framework controlla il ciclo di vita delle attività, la presenza di una delle attività partecipanti comporta il rischio di errori di runtime quando il proprietario viene distrutto prima dei suoi clienti. Se l'Applicazione lo possiede, alcune Attività devono rimuovere esplicitamente la voce per evitare che l'hashmap si attacchi alle voci con una chiave valida e un riferimento debole raccolto potenzialmente confuso. Inoltre, cosa dovrebbe fare un client quando il valore restituito per una chiave è null?

Mi sembra che una WeakHashMap di proprietà dell'applicazione o all'interno di un singleton sia una scelta migliore. Un valore nella mappa è accessibile tramite un oggetto chiave e quando non esistono riferimenti forti alla chiave (ovvero tutte le attività vengono eseguite con la chiave e ciò a cui mappa), GC può reclamare la voce della mappa.


2

Esistono vari modi per condividere i dati tra le attività

1: trasmissione di dati tra attività mediante Intent

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

2: usando una parola chiave statica, definisci la variabile come pubblica statica e usa qualsiasi posizione nel progetto

      public static int sInitialValue=0;

usare ovunque nel progetto usando classname.variableName;

3: Utilizzo del database

ma è un processo un po 'lungo, devi usare la query per inserire dati e iterare i dati usando il cursore quando necessario. Ma non è possibile perdere dati senza pulire la cache.

4: utilizzo delle preferenze condivise

molto più facile del database. ma c'è qualche limitazione che non puoi salvare ArrayList, List e oggetti personalizzati.

5: Crea setter getter nella classe Aplication e accedi a qualsiasi punto del progetto.

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

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

qui impostare e ottenere dalle attività

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

         String data=((YourApplicationClass)getApplicationContext()).getData();  

1

Bene, ho alcune idee, ma non so se sono ciò che stai cercando.

È possibile utilizzare un servizio che contiene tutti i dati e quindi associare le proprie attività al servizio per il recupero dei dati.

Oppure impacchettare i dati in un numero serializzabile o in pacchi e collegarli a un pacchetto e passare il pacchetto tra le attività.

Questo potrebbe non essere affatto quello che stai cercando, ma potresti anche provare a utilizzare un SharedPreferences o una preferenza in generale.

In ogni caso fammi sapere cosa decidi.



1

Se la tua intenzione è quella di chiamare altre attività dall'attività corrente, dovresti usare Intenti . La tua attenzione potrebbe essere meno sulla persistenza dei dati che sulla condivisione secondo necessità.

Tuttavia, se è davvero necessario persistere questi valori, è possibile persistere in una sorta di file di testo strutturato o database nella memoria locale. Un file delle proprietà, un file XML o un file JSON potrebbe archiviare i tuoi dati ed essere facilmente analizzato durante la creazione di attività. Non dimenticare inoltre che hai SQLite su tutti i dispositivi Android, in modo da poterli archiviare in una tabella di database. È inoltre possibile utilizzare una mappa per archiviare coppie chiave-valore e serializzare la mappa nella memoria locale, ma ciò potrebbe essere troppo ingombrante per essere utile per semplici strutture di dati.


1

Tutte le risposte di cui sopra sono fantastiche ... Sto solo aggiungendo una che nessuno aveva ancora menzionato sul persistere dei dati attraverso le attività e cioè di utilizzare il database SQLite Android incorporato per persistere i dati rilevanti ... In effetti puoi posizionare il tuo databaseHelper nello stato dell'applicazione e chiamalo come necessario durante le attivazioni .. O semplicemente crea una classe helper ed effettua le chiamate DB quando necessario ... Basta aggiungere un altro livello da considerare ... Ma tutte le altre risposte sarebbero sufficienti anche .. Davvero solo la preferenza


1

Condivisione dei dati tra le attività esempio passando un'e-mail dopo il login

"email" è il nome che può essere utilizzato per fare riferimento al valore sull'attività richiesta

1 Codice nella pagina di accesso

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

2 codice sulla home page

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");

1

E se vuoi lavorare con un oggetto dati, questi due strumenti sono molto importanti:

Serializable vs Parcelable

  • Serializable è un'interfaccia marker, che implica che l'utente non può eseguire il marshalling dei dati in base alle proprie esigenze. Quindi quando l'oggetto implementa Serializable Java lo serializzerà automaticamente.
  • Parcelable è il protocollo di serializzazione di Android. In Parcelable, gli sviluppatori scrivono codice personalizzato per il marshalling e il unmarshaling. Quindi crea meno oggetti spazzatura rispetto alla serializzazione
  • Le prestazioni di Parcelable sono molto elevate rispetto a Serializable a causa della sua implementazione personalizzata Si consiglia vivamente di utilizzare l'impianto Parcelable quando si serializzano oggetti in Android.

public class User implements Parcelable

controlla di più qui

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.