infinite state machine

Objekte per Intent-Extra übergeben

Introduction

user

Franziska Neumeister

Hat "Media Systems" studiert, entwickelt mobile Apps und will wissen, ob Androiden auch von elektrischen Schafen träumen


LATEST POSTS

Multithreading mit RxJava 18th April, 2016

Testbaren Code schreiben (Teil 1) 23rd April, 2015

Android

Objekte per Intent-Extra übergeben

Posted on .

Es gibt viele Möglichkeiten, den Zugriff auf Objekte zwischen verschiedenen Activity– und Service-Instanzen zu teilen. Die simpelste Lösung ist das Singleton-Entwurfsmuster oder ein statisches Feld, das ein Objekt hält und von verschiedenen Activities abgerufen werden kann.

// global zugängliches Objekt durch statisches Feld
User user = UserHelper.currentUser;
// gloabal zugängliches Objekt mit dem
// Singleton-Muster
ShoppingCart orders = ShoppingCart.getSharedInstance();

Schon einen Schritt aufwendiger ist eine eigene Application-Klasse für die App zu definieren, die als Attribute die Objekte hält, die zwischen Activities hin und hergereicht werden sollen. Theoretisch existieren diese Instanzen so lange, wie das Application-Objekt selbst.

MyCustomApplication app = (MyCustomApplication) this.getApplication();
Document doc = app.getCurrentDocument();

Der Nachteil aller dieser Ansätze ist, dass die geteilten Objekte dauerhaft Speicher belegen. Sie gehen verloren, falls im Hintergrund die komplette App beendet wird, z.B. durch Speichermangel. Ggf. wird die App dann nicht mehr richtig wiederhergestellt, wenn sie erneut in den Vordergrund kommt.

Eine weitere Möglichkeit ist Daten in dem Bundle-Objekt des Intents mit dem Namen „extras“ an eine andere Activity oder einen Service weiter zu geben. Ein Bundle nimmt alle primitiven Datentypen und CharSequence-Objekte an, sowie Objekte, die entweder die Schnittstelle Serializable oder Parcelable implementieren.

Die Technik Daten in einem Bundle zu speichern wird auch in der Methode onSaveInstanceState() der Activity-Klasse verwendet, wenn eine Activity beendet wird und ihr Zustand später wieder hergestellt werden soll, z.B. wenn die Bildschirmausrichtung sich ändert.

Serializable

Der einfachste Weg, ein Objekt in einem Bundle zu speichern, ist es serialisierbar zu machen, in dem es die Schnittstelle Serializable implementiert. Diese Schnittstelle erfordert keine weiteren Implementierungen und ist nur dazu da, dem System zu sagen, dass es den Zustand des Objekts durch Reflexion auszulesen soll.

public class Customer implements Serializable{
   String firstName;
   String lastName;
}

Der Aufwand für diese Technik ist minimal, die Klasse und jede Klasse, die sie als Attribut besitzt, muss lediglich die entsprechende Schnittstelle implementieren. Wenn auf diese weise nur wenige Objekte serialisiert werden, ist dieser Ansatz vollkommen ausreichend. Die Technik ist allerdings sehr performance-intensiv durch den Einsatz von Reflexion. Wenn viele Objekte und umfangreiche Objekt-Bäume serialisiert und deserialisiert werden, kann es zu wahrnehmbaren Verzögerungen kommen beim Start einer Activity.

Parcelable

Android bietet als Alternative zur Serializable die Schnittstelle Parcelable, die eine sehr viel performantere Serialisierung bietet. Allerdings ist sie auch aufwendiger bei der Implementierung und Wartung.

Eine Klasse, die als Parcel serialisiert werden kann, muss zwei Bedingungen erfüllen:

  1. die Schnittstelle Parcelable implementieren
  2. ein statisches Feld mit dem Namen CREATOR besitzen mit einer Objekt, dass die Schnittstelle Parcelable.Creator implementiert

Die Methode writeToParcel() beschreibt, wie der Zustand des Objekts in ein Parcel verpackt wird. Die zweite Methode der Parcelable Schnittstelle describeContents() gibt im Normalfall immer 0 zurück.

Die Implementierung der Creator Schnittstelle beschreibt in der createFromParcel() Methode hingegen, wie der Inhalt eines Parcels wiederum entpackt wird, um eine neue Instanz zu erzeugen, die den selben Zustand hat wie das serialisierte Objekt. Die Daten werden aus dem Parcel in der selben Reihenfolge ausgelesen, wie sie in writeToParcel() in das Parcel-Objekt geschrieben wurden. Auch bei dieser Schnittstelle ist die zweite Methode der Schnittstelle wenig interessant, weil sie in der Regel immer ein Array bestimmter Größe vom Typ der Klasse mit uninitialisiertem Inhalt erzeugt.

public class Customer implements Parcelable{
    String firstName;
    String lastName;

    public String getFirstName() {return firstName;}
    public String getLastName() {return lastName;}
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // die Attribute des Objekts wird in ein 
        // Parcel-Objekt geschrieben
        dest.writeString(firstName);
        dest.writeString(lastName);
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public Customer createFromParcel(Parcel source) {
            // ein neues Objekt wird erzeugt und mit den Daten aus 
            // dem Parcel initialisiert
            Customer obj = new Customer();
            obj.setFirstName(source.readString());
            obj.setLastName(source.readString());
            return obj;
        }

        @Override
        public Customer[] newArray(int size) {
            return new Customer[size];
        }
    };
}

Parceler Bibliothek

Parcler ist eine Bibliothek von John Ericksen, die den nötigen Code zur Implementierung der Parcelable Schnittstellen automatisch generiert. Um sie zu benutzen, muss man sie lediglich als neue Abhängigkeit in der build.gradle Datei eintragen:

 dependencies {
    // [...]
    compile "org.parceler:parceler-api:0.2.15"
    provided "org.parceler:parceler:0.2.15"
}

Um eine Klasse mit Parceler zu persistieren, muss sie nur mit @Parcel markiert werden.

@Parcel
public class Customer{
    String firstName;
    String lastName;
}

Um jetzt eine Instanz als Parcelable den Extras eines Intents hinzuzufügen ist noch die Methode Parcels.wrap() nötig.

Parcelable parcel = Parcels.wrap(data);
intent.putExtra(“extra_customer“, parcel);
startActivity(intent);

Umgekehrt braucht man noch die Methode Parcels.unwrap(), das gesuchte Objekt wieder aus dem Parcelable zu erhalten, dass von Parceler generiert wurde.

Parcelable extra = extras.getParcelable(“extra_customer“);
Customer customer = Parcels.unwrap(extra);

Sollen Attribute nicht serialisiert werden, kann man sie mit der Annotation @Transient markieren, damit sie von Parceler ignoriert.

@Parcel
public class Customer{
    String firstName;
    String lastName;
    @Transient
    String fullName;
}

Parceler benutzt standardmäßig einen leeren Konstruktor. Soll ein anderer benutzt werden kann er mit der @ParcelConstructor Annotation markiert werden.

@Parcel
public class Customer{
    String firstName;
    String lastName;

    public Customer() {}

    @ParcelConstructor
    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
 }
profile

Franziska Neumeister

Hat "Media Systems" studiert, entwickelt mobile Apps und will wissen, ob Androiden auch von elektrischen Schafen träumen

Navigation