So senden Sie Objekte per Bundle

119

Ich muss einen Verweis auf die Klasse übergeben, die den größten Teil meiner Verarbeitung über ein Bundle ausführt.

Das Problem ist, dass es nichts mit Absichten oder Kontexten zu tun hat und eine große Anzahl nicht-primitiver Objekte enthält. Wie packe ich die Klasse in ein Paket / serialisierbar und übergebe sie an ein startActivityForResult?

ahodder
quelle
2
"Ich muss einen Verweis auf die Klasse übergeben, die den größten Teil meiner Verarbeitung über ein Bundle ausführt" - warum?
CommonsWare
1
Ich habe ein Objekt (DataManager), es verwaltet einen Server und führt einige Backends für einige GUIs aus. Immer wenn eine neue Verbindung hergestellt wird, möchte ich, dass der Benutzer eine neue Aktivität starten kann, in der alle aktiven Verbindungen in ListView aufgelistet sind und der Benutzer eine auswählen kann. Die resultierenden Daten werden dann mit einer neuen GUI verknüpft. Dies ist wirklich nur eine Hautauswahl für das Backend.
Ahodder
3
Wenn Sie dieselbe Instanz eines Objekts über mehrere Aktivitäten hinweg bearbeiten, sollten Sie das Singleton-Muster berücksichtigen . Es gibt ein gutes Tutorial hier .
Sotrh

Antworten:

55

Um herauszufinden, welchen Weg Sie einschlagen müssen, müssen Sie nicht nur die Schlüsselfrage von CommonsWare nach dem "Warum", sondern auch die Frage nach dem "Wozu" beantworten. Passierst du es?

Die Realität ist, dass das einzige, was Bündel durchlaufen kann, einfache Daten sind - alles andere basiert auf Interpretationen dessen, was diese Daten bedeuten oder auf was sie verweisen. Sie können ein Objekt nicht buchstäblich passieren, aber was Sie tun können, ist eines von drei Dingen:

1) Sie können das Objekt in seine konstituierten Daten zerlegen. Wenn das andere Objekt Kenntnis von derselben Art von Objekt hat, kann es aus den serialisierten Daten einen Klon zusammenstellen. So durchlaufen die meisten gängigen Typen Bündel.

2) Sie können einen undurchsichtigen Griff passieren. Wenn Sie es im selben Kontext übergeben (obwohl man sich fragen könnte, warum Sie sich die Mühe machen), ist dies ein Handle, das Sie aufrufen oder dereferenzieren können. Wenn Sie es jedoch über Binder an einen anderen Kontext übergeben, ist der Literalwert eine beliebige Zahl (tatsächlich zählen diese willkürlichen Zahlen vom Start an nacheinander). Sie können nichts anderes tun, als den Überblick zu behalten, bis Sie ihn an den ursprünglichen Kontext zurückgeben, wodurch Binder ihn wieder in den ursprünglichen Handle umwandelt und ihn wieder nützlich macht.

3) Sie können ein magisches Handle übergeben, z. B. einen Dateideskriptor oder einen Verweis auf bestimmte Betriebssystem- / Plattformobjekte. Wenn Sie die richtigen Flags setzen, erstellt Binder einen Klon, der auf dieselbe Ressource für den Empfänger verweist und für die tatsächlich verwendet werden kann das andere Ende. Dies funktioniert jedoch nur für sehr wenige Objekttypen.

Höchstwahrscheinlich übergeben Sie Ihre Klasse entweder nur, damit das andere Ende sie verfolgen und später wiedergeben kann, oder Sie übergeben sie an einen Kontext, in dem ein Klon aus serialisierten Bestandteilen erstellt werden kann ... oder sonst Sie versuchen, etwas zu tun, das einfach nicht funktioniert, und Sie müssen den gesamten Ansatz überdenken.

Chris Stratton
quelle
1
Danke für die Antwort auf die Tour. Ihr Recht, alles was ich tun muss, ist nur eine Referenz einer Liste von Objekten an meine neue Aktivität zu übergeben. Die neue Aktivität nimmt einige Daten aus der Liste und zeigt eine auswählbare ListView an. onSelect gibt die Aktivität ein Ergebnis (einige Daten zum Klickobjekt) an die Hostaktivität zurück. Wenn ich das richtig verstehe, glaube ich, dass Ihre Option 2 dies am besten handhabt. Wie bekomme ich diesen undurchsichtigen Griff?
Ahodder
Ihre andere Aktivität kann keine Daten aus einem undurchsichtigen Objekt extrahieren, das angezeigt werden soll. Was Sie wahrscheinlich tun möchten, ist, einige Ersatzobjekte eines unterstützten Typs zu erstellen und zu übergeben, die Kopien der angezeigten Informationen enthalten.
Chris Stratton
158

Sie können Gson auch verwenden, um ein Objekt in ein JSONObject zu konvertieren und es an ein Bundle weiterzugeben. Für mich war der eleganteste Weg, dies zu tun. Ich habe nicht getestet, wie sich dies auf die Leistung auswirkt.

In der ersten Aktivität

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

In der nächsten Aktivität

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);
Laranjeiro
quelle
3
Da es darum geht, Dinge zwischen Aktivitäten auszutauschen, kommt es nicht oft genug vor, um einen großen Einfluss auf die Gesamtleistung der App zu haben. Abgesehen davon bezweifle ich, dass es funktionieren würde, den DataManager des ursprünglichen Beitrags zu serialisieren, da es so klingt, als hätte er Socket-Verbindungen und andere ähnliche Klassen.
Britzl
4
Auch Google schlägt diese Lösung anstelle von Serialize vor:
Lesen Sie
3
Nur als Warnung habe ich diese Technik eine Weile befolgt, aber es gibt eine Speicherbeschränkung für das, was Sie als String übergeben können. Stellen Sie also sicher, dass Ihre Daten nicht zu groß sind.
Jiduvah
Unter blog.madadipouya.com/2015/09/21/… erfahren Sie, wie Sie Ihrem Projekt Gson-Unterstützung hinzufügen können.
GeekQ
Intelligente Lösung, Hut ab vor Ihnen!
Rohit Mandiwal
20

Die Parcelable- Oberfläche ist eine gute Möglichkeit, ein Objekt mit einer Absicht zu übergeben.

Wie kann ich meine benutzerdefinierten Objekte paketierbar machen? ist eine ziemlich gute Antwort auf die Verwendung von Parcelable

Die offiziellen Google Docs enthalten auch ein Beispiel

chris
quelle
1
oder sie können auch serialisierbar sein.
Jeffrey Blattman
1
Reduziert aber die Leistung um das 10-fache !! Schauen Sie sich diesen Benchmark an: developerphil.com/parcelable-vs-serializable
saiyancoder
2
+1 @ Matis Kommentar, jedoch 10x in den Kontext zu setzen, wenn er auf ein einzelnes Objekt angewendet wird, entspricht 1 ms. Also vielleicht nicht so schlimm wie es sich anhört.
Pinoyyid
1
Zustimmen. Das Problem besteht darin, dass Sie sich mit Sammlungen befassen. Dies ist ein sehr häufiger Anwendungsfall, wenn Sie Ressourcen von einer Rest-API abrufen. Aber für ein einzelnes Objekt sollte es nicht berüchtigt sein. Wie auch immer, wenn Ihnen der gesamte Boilerplate-Code im Weg steht, können Sie diese Bibliothek ausprobieren, die alles für Sie generiert: github.com/johncarl81/parceler . Ein wirklich schöner Ansatz!
Saiyancoder
Unterbrochener Link: 404 (nicht gefunden)
Gallal
14

Sie können den globalen Anwendungsstatus verwenden .

Aktualisieren:

Passen Sie dies an und fügen Sie es dann Ihrer AndroidManifest.xml hinzu:

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

Und dann haben Sie eine Klasse in Ihrem Projekt wie folgt:

package com.example;

import android.app.Application;

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

Und weil " Es kann über getApplication () von jeder Aktivität oder jedem Dienst aus zugegriffen werden ", verwenden Sie es folgendermaßen:

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

Hoffentlich hilft das.

Neil
quelle
1
Danke für die Antwort, aber wie?
Ahodder
Ich glaube, Sie unterordnen nur die Anwendung und können dann alles speichern, was Sie möchten. Die XML-Änderungen, die Sie benötigen, sind im obigen Link aufgeführt.
Mark Storer
9
Als allgemeines Designprinzip ist es eine gute Idee, Globals zu vermeiden, es sei denn, Sie brauchen sie wirklich. In diesem Fall gibt es gute Alternativen.
Dhaag23
Ok, ich glaube ich verstehe was du sagst. Erweitern Sie einfach Application und geben Sie eine Variable ein, die das Objekt enthält, das übergeben werden muss. Ich habe die Referenzseite überprüft und die erforderlichen XML-Änderungen nicht gesehen.
Ahodder
Ich wollte das auch als Antwort schreiben. Dies ist definitiv eine der Möglichkeiten, dies zu tun. Beachten Sie jedoch, dass diese Objekte im Speicher verbleiben, es sei denn, Sie de-aktualisieren sie (oder der App-Kontext wird zerstört) und können Speicherplatz belegen, wenn Sie sie nicht benötigen.
Igor Čordaš
12

Sie können Ihre Objekte auch serialisierbar machen und die Methoden getSerializable und putSerializable des Bundles verwenden.

dhaag23
quelle
1
Ich versuchte das und erkannte schnell, dass es unpraktisch sein würde. Ich denke nicht, dass die meisten Objekte, die in übergebenen Klassen (Threads) gespeichert sind, serialisierbar sind. :) trotzdem danke.
Ahodder
10

Mögliche Lösung:

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

Klasse CustomObject:

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

Unterkundenobjekte:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }
PraKhar
quelle
7

Eine weitere Möglichkeit, Objekte über ein Bundle zu senden, ist die Verwendung von bundle.putByteArray
Beispielcode

public class DataBean implements Serializable {
private Date currentTime;

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

public Date getCurrentTime() {
    return currentTime;
 }
}

Objekt von DataBean in Bundle einfügen:

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

Konvertieren von Objekten in Byte-Arrays

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

Objekt vom Bundle zurückholen:

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

Methode zum Abrufen von Objekten aus Byte-Arrays:

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

Hoffe das wird anderen Kumpels helfen.

Rupesh Yadav
quelle
Dies sieht glatt und einfach aus, wenn man den Code betrachtet. Aber ich denke, es gibt noch etwas mehr darüber, warum das SDK so etwas nicht zum Übergeben von Objekten anbietet. Können Sie mir etwas mehr zu dieser Lösung erzählen?
Mario Lenci
3
Der ganze Code ist überhaupt nicht nötig! Verwenden Sie bundle.putSerializable (objectImplementingSerializable) - dies geschieht unter dem, was Sie hier erneut implementieren ...
Risadinha
3

1. Ein sehr direktes und einfach zu verwendendes Beispiel: Machen Sie das zu übergebende Objekt serialisierbar.

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

2. Objekt im Bundle übergeben

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

3.get übergebenes Objekt aus dem Bundle als serialisierbar und dann in Objekt umgewandelt.

Object object = (Object) getArguments().getSerializable("object");
king_T
quelle
0

Dies ist eine sehr verspätete Antwort auf meine eigene Frage, aber sie wird immer wieder beachtet, daher muss ich sie ansprechen. Die meisten dieser Antworten sind richtig und erledigen den Job perfekt. Dies hängt jedoch von den Anforderungen der Anwendung ab. Diese Antwort wird verwendet, um zwei Lösungen für dieses Problem zu beschreiben.

Anwendung

Die erste ist die Anwendung , da hier am meisten über die Antwort gesprochen wurde. Die Anwendung ist ein gutes Objekt, um Entitäten zu platzieren, die einen Verweis auf einen Kontext benötigen. Ein "ServerSocket" würde zweifellos einen Kontext benötigen (für Datei-E / A oder einfache "ListAdapter" -Updates). Ich persönlich bevorzuge diese Route. Ich mag Anwendungen, sie sind nützlich für das Abrufen von Kontexten (weil sie statisch gemacht werden können und wahrscheinlich keinen Speicherverlust verursachen) und haben einen einfachen Lebenszyklus.

Bedienung

Der Service` ist an zweiter Stelle. Ein "Service" ist eigentlich die bessere Wahl für mein Problem, da genau dafür Services entwickelt wurden:
Ein Service ist eine Anwendungskomponente, in der lang laufende Vorgänge ausgeführt werden können
den Hintergrund und bietet keine Benutzeroberfläche.
Services sind insofern ordentlich, als sie einen definierten Lebenszyklus haben, der einfacher zu steuern ist. Darüber hinaus können Dienste bei Bedarf extern von der Anwendung ausgeführt werden (dh beim Booten). Dies kann für einige Apps oder nur für eine nette Funktion erforderlich sein.

Dies war auch keine vollständige Beschreibung, aber ich habe Links zu den Dokumenten für diejenigen hinterlassen, die mehr untersuchen möchten. Insgesamt Serviceist das besser für die Instanz, die ich brauchte - ein ServerSocket auf meinem SPP-Gerät ausführen.

ahodder
quelle
0

Ich bin auf diese Frage gestoßen, als ich nach einer Möglichkeit gesucht habe, ein Date-Objekt zu übergeben. In meinem Fall habe ich, wie in den Antworten vorgeschlagen, Bundle.putSerializable () verwendet, aber das würde für eine komplexe Sache wie den im ursprünglichen Beitrag beschriebenen DataManager nicht funktionieren.

Mein Vorschlag, der ein sehr ähnliches Ergebnis wie das Einfügen des DataManagers in die Anwendung oder das Erstellen eines Singletons liefert, besteht darin, die Abhängigkeitsinjektion zu verwenden, den DataManager an einen Singleton-Bereich zu binden und den DataManager dort einzufügen, wo er benötigt wird. Sie profitieren nicht nur von einer verbesserten Testbarkeit, sondern erhalten auch einen saubereren Code, ohne dass der gesamte Code der Kesselplatte "Abhängigkeiten zwischen Klassen und Aktivitäten weitergeben". (Robo) Guice ist sehr einfach zu bearbeiten und das neue Dolch- Framework sieht ebenfalls vielversprechend aus.

britzl
quelle
1
Nun, mit so etwas wie einem Date könnten Sie einfach den langen Wert übergeben. Aber der Rest klingt gut. Vielen Dank.
Ahodder
0

Eine weitere einfache Möglichkeit, ein Objekt mithilfe eines Bundles zu übergeben:

  • Erstellen Sie im Klassenobjekt eine statische Liste oder eine andere Datenstruktur mit einem Schlüssel
  • Wenn Sie das Objekt erstellen, fügen Sie es mit dem Schlüssel in die Listen- / Datenstruktur ein (z. B. den langen Zeitstempel, wenn das Objekt erstellt wird).
  • Erstellen Sie die statische Methode getObject (langer Schlüssel), um das Objekt aus der Liste abzurufen
  • Übergeben Sie im Bundle den Schlüssel, damit Sie das Objekt später an einer anderen Stelle im Code abrufen können
Shaolin
quelle