Was ist der beste Weg, um Daten zwischen Aktivitäten auszutauschen?

239

Ich habe eine Aktivität, die die Hauptaktivität ist, die in der App verwendet wird, und sie hat eine Reihe von Variablen. Ich habe zwei weitere Aktivitäten, bei denen ich die Daten der ersten Aktivität verwenden möchte. Jetzt weiß ich, dass ich so etwas tun kann:

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

Ich möchte jedoch viele Variablen gemeinsam nutzen und einige sind möglicherweise ziemlich groß, sodass ich keine Kopien wie oben erstellen möchte.

Gibt es eine Möglichkeit, die Variablen direkt abzurufen und zu ändern, ohne die Methoden get und set zu verwenden? Ich erinnere mich, dass ich in einem Artikel auf der Google Dev-Website gelesen habe, dass dies für die Leistung unter Android nicht empfohlen wird.

Verrückter Idiot
quelle
2
Ab Android 2.3 (Gingerbread) wird die Optimierung von get / set automatisch von Dalvik durchgeführt. Dies ist nur relevant, wenn Sie auf ältere Android-Versionen abzielen.
StellarVortex
Beachten Sie, dass das Beispiel die Zeichenfolgendaten nicht kopiert. Vielmehr wird ein Verweis auf dasselbe Zeichenfolgenobjekt erstellt.
Code-Apprentice
Es ist kaum zu glauben, warum es keine Möglichkeit gibt, eine Aktivität von einer anderen Aktivität aus zu starten und ein komplexes Objekt von der ersten zur zweiten zu übergeben. Speichern Sie ohne Serialisierung das Objekt und all diesen Aufwand. Ist dies eine Sicherheitslücke oder welcher andere Grund spricht dagegen, die Objektreferenz einfach zu übergeben, wenn sich beide Aktivitäten in derselben App befinden? (Ich verstehe, es ist anders, wenn sie in verschiedenen Apps sind)
Droidum
Bitte sehen Sie diese stackoverflow.com/questions/56521969/…
Levon Petrosyan
LiveData ist die beste und neueste Lösung. Überprüfen Sie meine Antwort unten.
Amir Uval

Antworten:

475

Hier eine Zusammenstellung der gängigsten Möglichkeiten, um dies zu erreichen :

  • Senden Sie Daten innerhalb der Absicht
  • Statische Felder
  • HashMap von WeakReferences
  • Objekte beibehalten (SQLite, Freigabeeinstellungen, Datei usw.)

TL; DR : Es gibt zwei Möglichkeiten, Daten gemeinsam zu nutzen: Daten in den Extras der Absicht übergeben oder an einem anderen Ort speichern. Wenn es sich bei Daten um Grundelemente, Zeichenfolgen oder benutzerdefinierte Objekte handelt: Senden Sie sie als Teil der Absichts-Extras (benutzerdefinierte Objekte müssen implementiert werden Parcelable). Wenn Sie komplexe Objekte übergeben, speichern Sie eine Instanz in einem Singleton an einem anderen Ort und greifen Sie über die gestartete Aktivität darauf zu.

Einige Beispiele, wie und warum jeder Ansatz implementiert werden soll:

Senden Sie Daten innerhalb von Absichten

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

Zur zweiten Aktivität:

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

Verwenden Sie diese Methode, wenn Sie primitive Daten oder Strings übergeben . Sie können auch Objekte übergeben, die implementiert werden Serializable.

Obwohl verlockend, sollten Sie vor der Verwendung zweimal überlegen Serializable: Es ist fehleranfällig und schrecklich langsam. Also generell: meideSerializable wenn möglich. Wenn Sie komplexe benutzerdefinierte Objekte übergeben möchten, sehen Sie sich die ParcelableBenutzeroberfläche an . Es ist schwieriger zu implementieren, hat aber im Vergleich zu erhebliche Geschwindigkeitsgewinne Serializable.

Teilen Sie Daten, ohne auf der Festplatte zu bleiben

Es ist möglich, Daten zwischen Aktivitäten auszutauschen, indem sie im Speicher gespeichert werden, da in den meisten Fällen beide Aktivitäten im selben Prozess ausgeführt werden.

Hinweis: Manchmal, wenn der Benutzer Ihre Aktivität verlässt (ohne sie zu beenden), entscheidet sich Android möglicherweise dafür, Ihre Anwendung zu beenden. In einem solchen Szenario habe ich Fälle erlebt, in denen Android versucht, die letzte Aktivität mit der vor dem Beenden der App angegebenen Absicht zu starten. In diesem Fall gehen die in einem Singleton gespeicherten Daten (entweder Ihre oder Ihre Application) verloren und es können schlimme Dinge passieren. Um solche Fälle zu vermeiden, behalten Sie entweder Objekte auf der Festplatte bei oder überprüfen Daten, bevor Sie sie verwenden, um sicherzustellen, dass sie gültig sind.

Verwenden Sie eine Singleton-Klasse

Haben Sie eine Klasse, um die Daten zu halten:

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

Aus der gestarteten Aktivität:

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

Verwenden Sie die Anwendung Singleton

Der Anwendungs-Singleton ist eine Instanz, android.app.Applicationdie beim Start der App erstellt wird. Sie können eine benutzerdefinierte bereitstellen, indem Sie Folgendes erweitern 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;}
}

Vor dem Starten der Aktivität:

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

Dann aus der gestarteten Aktivität:

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

Statische Felder

Die Idee ist im Grunde die gleiche wie beim Singleton, aber in diesem Fall bieten Sie statischen Zugriff auf die Daten:

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

Aus der gestarteten Aktivität:

String data = DataHolder.getData();

HashMap von WeakReferences

Dieselbe Idee, aber dem Garbage Collector erlauben, nicht referenzierte Objekte zu entfernen (z. B. wenn der Benutzer die Aktivität beendet):

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

Vor dem Starten der Aktivität:

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

Aus der gestarteten Aktivität:

DataHolder.getInstance().retrieve(someId);

Möglicherweise müssen Sie die Objekt-ID mithilfe der Extras der Absicht übergeben oder nicht. Es hängt alles von Ihrem spezifischen Problem ab.

Objekte auf der Festplatte beibehalten

Die Idee ist, die Daten auf der Festplatte zu speichern, bevor die andere Aktivität gestartet wird.

Vorteile: Sie können die Aktivität von anderen Orten aus starten. Wenn die Daten bereits gespeichert sind, sollten sie einwandfrei funktionieren.

Nachteile: Die Implementierung ist umständlich und dauert länger. Benötigt mehr Code und damit mehr Chancen, Fehler einzuführen. Es wird auch viel langsamer sein.

Einige der Möglichkeiten, Objekte beizubehalten, umfassen:

Cristian
quelle
11
Ich würde argumentieren, dass dies nicht der "normale" Weg für größere / komplexere Daten ist. Es ist viel einfacher, einen statischen Singleton oder das Application-Objekt zu verwenden, und es funktioniert hervorragend. Nachdem das OP im Beispiel einen String verwendet hat, ist Intent perfekt und bevorzugt.
Charlie Collins
10
Serializable hat schwerwiegende Leistungsprobleme beim Android-Prozessmodell festgestellt. Deshalb haben sie Parcelable eingeführt. Lesen Sie in der obigen Antwort Parcelable statt Serializable .
Subin Sebastian
3
Das geschieht über die setResultMethode. In diesem Fall muss die sekundäre Aktivität auch mithilfe der startActivityForResultMethode aufgerufen werden .
Cristian
2
Tolle Zusammenfassung! In Bezug auf das Problem der Zerstörung von Singletons gibt es eine einfache Lösung für Apps mit vielen Aktivitäten und Objekten: Verwenden Sie eine Unterklasse von Activitydurchgehend und onCreate()überprüfen Sie bei der Überprüfung jedes statische Feld des Singletons, das Sie beim Starten der App ausfüllen. Wenn dieses Feld leer ist, kehren Sie mit FLAG_ACTIVITY_CLEAR_TASKoder a BroadcastReceiverzur Startaktivität zurück, um andere Aktivitäten zu beenden.
Janosch
1
Ich würde nicht empfehlen, Daten in der Anwendungsklasse zu speichern. Lesen Sie hier mehr: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan
22

Was Sie verwenden können:

  1. Daten zwischen Aktivitäten weitergeben (wie Cristian sagte)
  2. Verwenden einer Klasse mit vielen statischen Variablen (Sie können sie also ohne Instanz der Klasse und ohne Verwendung von Getter / Setter aufrufen)
  3. Verwenden einer Datenbank
  4. Gemeinsame Einstellungen

Was Sie wählen, hängt von Ihren Bedürfnissen ab. Wahrscheinlich werden Sie mehr als einen Weg benutzen, wenn Sie "viel" haben.

WarrenFaith
quelle
1
Bitte beachten Sie, dass die Statik beim Prozesstod gelöscht wird
EpicPandaForce
@EpicPandaForce natürlich und auch wenn das Gerät ausgeschaltet war.
WarrenFaith
1
Wenn das Gerät jedoch ausgeschaltet ist, wird die App von der MAINAktion aus neu gestartet . Nach dem Prozessstart starten Sie die zuletzt geöffnete Aktivität neu. Dies kann eine Detailseite irgendwo tief in der App sein.
EpicPandaForce
@EpicPandaForce scheint, dass Sie meine Ironie verpasst haben, obwohl Sie Cpt offensichtlich sind.
WarrenFaith
16

Tun Sie, was Google Ihnen befiehlt! hier: http://developer.android.com/resources/faq/framework.html#3

  • Primitive Datentypen
  • Nicht persistente Objekte
  • Singleton-Klasse - mein Favorit: D.
  • Ein öffentliches statisches Feld / eine öffentliche statische Methode
  • Eine HashMap mit schwachen Referenzen zu Objekten
  • Persistente Objekte (Anwendungseinstellungen, Dateien, Inhaltsanbieter, SQLite-Datenbank)
Tylko Arka Gdynia
quelle
1
Google Link für persistente Objekte: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk
Nur Antimuster, Singleton-Klasse ist das erste Entwurfsmuster, eine Klasse mit statischem Feld / statischer Methode verhält sich ähnlich wie Singletons und das Erstellen einer Datenbank für persistierende Busniss-Objekte ist manchmal keine gute Sache, natürlich ist es nicht Ihre Schuld und Sie sind eine Auflistung möglich Möglichkeiten, es zu erreichen, aber ich frage mich, warum Google so eine sehr dumme Sache kompliziert hat, Leistungsprobleme oder was ?? !!!! oder verstehe ich den Android-Weg nicht ?? !!!!
La VloZ Merrill
Link ist defekt :(
Shayan_Aryan
14

"Ich möchte jedoch viele Variablen gemeinsam nutzen, und einige sind möglicherweise ziemlich groß, sodass ich keine Kopien wie oben erstellen möchte."

Das macht keine Kopie (besonders mit String , aber selbst Objekte werden als Wert der Referenz übergeben, nicht des Objekts selbst, und solche Getter sind in Ordnung zu verwenden - wohl besser zu verwenden als andere Mittel, weil sie gemeinsam sind und gut verstanden). Die älteren "Leistungsmythen", wie das Verzichten auf Getter und Setter, haben immer noch einen gewissen Wert, wurden aber auch in den Dokumenten aktualisiert .

Wenn Sie dies jedoch nicht möchten, können Sie die Variablen auch einfach in GlobalState öffentlich oder geschützt machen und direkt darauf zugreifen. Und Sie können einen statischen Singleton erstellen, wie das Anwendungsobjekt JavaDoc angibt :

Normalerweise muss die Anwendung nicht in Unterklassen unterteilt werden. In den meisten Situationen können statische Singletons dieselbe Funktionalität auf modularere Weise bereitstellen. Wenn Ihr Singleton einen globalen Kontext benötigt (zum Beispiel zum Registrieren von Rundfunkempfängern), kann der Funktion zum Abrufen ein Kontext zugewiesen werden, der beim erstmaligen Erstellen des Singletons intern Context.getApplicationContext () verwendet.

Die Verwendung von Absichtsdaten , wie andere Antworten hier vermerken, ist eine andere Möglichkeit, Daten zu übergeben, wird jedoch normalerweise für kleinere Daten und einfache Typen verwendet. Sie können größere / komplexere Daten übergeben, dies ist jedoch aufwändiger als die Verwendung eines statischen Singleons. Das Anwendungsobjekt ist immer noch mein persönlicher Favorit für die gemeinsame Nutzung größerer / komplexerer nicht persistenter Daten zwischen Android-Anwendungskomponenten (da es in einer Android-App einen genau definierten Lebenszyklus aufweist).

Wie andere bereits bemerkt haben, können Sie auch SQLite oder das Dateisystem verwenden , wenn die Daten sehr komplex werden und persistent sein müssen .

Charlie Collins
quelle
1
Eigentlich bin ich kürzlich in den Dokumenten darauf gestoßen: developer.android.com/guide/appendix/faq/framework.html#3 . Für "nicht persistente komplexe Objekte" wird empfohlen, die Application-Klasse zum Erstellen und Herunterfahren eines statischen Singletons zu verwenden! Auf diese Weise erhalten Sie die genau definierte Lebenszyklusanwendung und die Benutzerfreundlichkeit eines statischen Singletons.
Charlie Collins
2
Dieser Abschnitt der FAQ scheint jetzt entfernt zu sein (ich sehe nur "nicht persistente Objekte" und keine Erwähnung der Anwendungsklasse). Wie auch immer können Sie näher darauf eingehen?
Tony Chan
1
Es befindet sich derzeit unter developer.android.com/guide/faq/framework.html#3 "Wie übergebe ich Daten zwischen Aktivitäten / Diensten innerhalb einer einzelnen Anwendung?" Und ohne Erwähnung der Anwendungsklasse.
Jerry101
Ich mag es, ein Anwendungsobjekt zu verwenden, das dem Präzedenzfall folgt, den Sie in "Android in Aktion" festgelegt haben. Aber viele potenzielle Arbeitgeber mögen es nicht, wenn sie dies in Code-Herausforderungen sehen. Sie könnten sich irren, aber sie werfen ihr Gewicht herum. Übrigens: Dieser Link 'framework.html # 3' funktioniert nicht mehr.
Matt J.
4

Es gibt eine neue und bessere Möglichkeit, Daten zwischen Aktivitäten auszutauschen , und es handelt sich um LiveData . Beachten Sie insbesondere dieses Zitat von der Android-Entwicklerseite:

Die Tatsache, dass LiveData-Objekte lebenszyklusabhängig sind, bedeutet, dass Sie sie für mehrere Aktivitäten, Fragmente und Dienste freigeben können. Um das Beispiel einfach zu halten, können Sie die LiveData-Klasse als Singleton implementieren

Dies hat enorme Auswirkungen: Alle Modelldaten können in einer gemeinsamen Singleton-Klasse in einem LiveDataWrapper gemeinsam genutzt werden. Es kann aus ViewModelGründen der Testbarkeit aus den Aktivitäten in ihre jeweiligen injiziert werden . Und Sie müssen sich keine Sorgen mehr über schwache Referenzen machen, um Speicherverluste zu vermeiden.

Amir Uval
quelle
2

Die Verwendung der oben beschriebenen Hashmap des schwachen Referenzansatzes und unter http://developer.android.com/guide/faq/framework.html erscheint mir problematisch. Wie werden ganze Einträge zurückgefordert, nicht nur der Kartenwert? In welchem ​​Umfang ordnen Sie es zu? Da das Framework die Kontrolle über den Aktivitätslebenszyklus hat und eine der teilnehmenden Aktivitäten besitzt, besteht die Gefahr von Laufzeitfehlern, wenn der Eigentümer vor seinen Kunden zerstört wird. Wenn die Anwendung Eigentümerin ist, muss eine Aktivität den Eintrag explizit entfernen, um zu verhindern, dass die Hashmap Einträge mit einem gültigen Schlüssel und einer möglicherweise in der Müllhalde gesammelten schwachen Referenz festhält. Was sollte ein Client außerdem tun, wenn der für einen Schlüssel zurückgegebene Wert null ist?

Es scheint mir, dass eine WeakHashMap, die der Anwendung oder einem Singleton gehört, die bessere Wahl ist. Auf einen Wert in der Karte wird über ein Schlüsselobjekt zugegriffen. Wenn keine starken Verweise auf den Schlüssel vorhanden sind (dh alle Aktivitäten werden mit dem Schlüssel und dessen Zuordnung ausgeführt), kann GC den Karteneintrag zurückfordern.

Mark Rosenberg
quelle
2

Es gibt verschiedene Möglichkeiten, Daten zwischen Aktivitäten auszutauschen

1: Übergeben von Daten zwischen Aktivitäten mit Intent

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

2: Definieren Sie die Variable mit dem Schlüsselwort static als public static und verwenden Sie eine beliebige Stelle im Projekt

      public static int sInitialValue=0;

Verwenden Sie irgendwo im Projekt mithilfe von classname.variableName.

3: Verwenden der Datenbank

Da der Prozess jedoch etwas langwierig ist, müssen Sie eine Abfrage zum Einfügen von Daten verwenden und Daten bei Bedarf mit dem Cursor iterieren. Es besteht jedoch keine Möglichkeit, Daten zu verlieren, ohne den Cache zu bereinigen.

4: Gemeinsame Einstellungen verwenden

viel einfacher als Datenbank. Es gibt jedoch einige Einschränkungen, dass Sie ArrayList-, List- und Custome-Objekte nicht speichern können.

5: Erstellen Sie einen Getter-Setter in der Aplication-Klasse und greifen Sie auf alle Stellen im Projekt zu.

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

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

hier setzen und von Aktivitäten bekommen

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

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

Nun, ich habe ein paar Ideen, aber ich weiß nicht, ob sie das sind, wonach Sie suchen.

Sie können einen Dienst verwenden, der alle Daten enthält, und dann Ihre Aktivitäten einfach an den Dienst binden, um Daten abzurufen.

Oder verpacken Sie Ihre Daten in eine serialisierbare oder paketierbare Datei, hängen Sie sie an ein Bundle an und übergeben Sie das Bundle zwischen den Aktivitäten.

Dieser ist möglicherweise überhaupt nicht das, wonach Sie suchen, aber Sie können auch versuchen, SharedPreferences oder eine Präferenz im Allgemeinen zu verwenden.

In jedem Fall lassen Sie mich wissen, was Sie entscheiden.

ahodder
quelle
1

Angenommen, Sie rufen Aktivität zwei von Aktivität eins mit einer Absicht auf.
Sie können die Daten mit dem intent.putExtra () übergeben.

Nehmen Sie dies als Referenz. Senden von Arrays mit Intent.putExtra

Hoffe das ist was du willst.

Manish Khot
quelle
1

Wenn Ihre Absicht andere Aktivitäten ist von der aktuellen Aktivität zu rufen, sollten Sie verwenden Intents . Ihr Fokus könnte weniger auf dem Bestehen von Daten als auf dem Teilen nach Bedarf liegen.

Wenn Sie diese Werte jedoch wirklich beibehalten müssen, können Sie sie in einer strukturierten Textdatei oder Datenbank im lokalen Speicher beibehalten. Eine Eigenschaftendatei, XML-Datei oder JSON-Datei kann Ihre Daten speichern und während der Aktivitätserstellung leicht analysiert werden. Vergessen Sie auch nicht, dass Sie SQLite auf allen Android-Geräten haben, damit Sie sie in einer Datenbanktabelle speichern können. Sie können auch eine Map verwenden, um Schlüssel-Wert-Paare zu speichern und die Map in den lokalen Speicher zu serialisieren. Dies ist jedoch möglicherweise zu umständlich, um für einfache Datenstrukturen nützlich zu sein.

Mike Yockey
quelle
1

Alle oben genannten Antworten sind großartig ... Ich füge nur eine hinzu, die noch niemand über das Fortbestehen von Daten durch Aktivitäten erwähnt hat, und das heißt, die integrierte Android SQLite-Datenbank zu verwenden, um relevante Daten beizubehalten ... Tatsächlich können Sie Ihre platzieren databaseHelper im Anwendungsstatus und rufen Sie ihn während der gesamten Aktivierung nach Bedarf auf. Oder erstellen Sie einfach eine Hilfsklasse und führen Sie die DB-Aufrufe bei Bedarf aus. Fügen Sie einfach eine weitere Ebene hinzu, die Sie berücksichtigen sollten. Alle anderen Antworten würden jedoch ausreichen auch .. Wirklich nur Präferenz

Benutzerbild
quelle
1

Das Teilen von Daten zwischen Aktivitäten, z. B. das Übergeben einer E-Mail nach dem Anmelden

"E-Mail" ist der Name, mit dem auf den Wert der angeforderten Aktivität verwiesen werden kann

1 Code auf der Anmeldeseite

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

2 Code auf der Homepage

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

Und wenn Sie mit Datenobjekten arbeiten möchten, sind diese beiden Implementierungen sehr wichtig:

Serializable vs Parcelable

  • Serialisierbar ist eine Markierungsschnittstelle, die impliziert, dass der Benutzer die Daten nicht gemäß seinen Anforderungen zusammenstellen kann. Wenn das Objekt Serializable implementiert, serialisiert Java es automatisch.
  • Parcelable ist ein Android-eigenes Serialisierungsprotokoll. In Parcelable schreiben Entwickler benutzerdefinierten Code zum Marshalling und Unmarshaling. Dadurch werden im Vergleich zur Serialisierung weniger Müllobjekte erstellt
  • Die Leistung von Parcelable ist im Vergleich zu Serializable aufgrund seiner benutzerdefinierten Implementierung sehr hoch. Es wird dringend empfohlen, die Parcelable-Implantation zu verwenden, wenn Objekte in Android serialisiert werden.

public class User implements Parcelable

Checken Sie hier mehr ein

Diego Venâncio
quelle