Haben Sie eine Frage? Fragen Sie es auf StackOverflow.
Haben Sie ein Problem gefunden? Bitte melden Sie es.
In Android sind Parcelables eine großartige Möglichkeit, Java-Objekte zwischen Kontexten zu serialisieren. Im Vergleich zur herkömmlichen Serialisierung benötigt Parcelables etwa zehnmal weniger Zeit für die Serialisierung und Deserialisierung. Es gibt jedoch einen großen Fehler bei Parcelables. Pakete enthalten eine Menge Standardcode. Um ein Parcelable zu implementieren, müssen Sie die Methoden writeToParcel()
und createFromParcel()
so spiegeln, dass sie das Parcel in derselben Reihenfolge lesen und schreiben. Außerdem muss ein Parcelable einen public static final Parcelable.Creator CREATOR
definieren, damit die Android-Infrastruktur den Serialisierungscode nutzen kann.
Parceler ist eine Codegenerierungsbibliothek, die den Boilerplate-Quellcode für Android Parcelable generiert. Sie müssen nicht mehr die Parcelable-Schnittstelle, writeToParcel()
oder createFromParcel()
oder den public static final CREATOR
implementieren. Sie kommentieren einfach ein POJO mit @Parcel
und Parceler erledigt den Rest. Da Parceler den Java JSR-269 Annotation Processor verwendet, ist es nicht erforderlich, manuell ein Tool auszuführen, um den Parcelable-Code zu generieren. Kommentieren Sie einfach Ihr Java Bean, kompilieren Sie es und schon sind Sie fertig. Standardmäßig serialisiert Parceler die Felder Ihrer Instanz direkt:
@ Parcel
public class Example {
String name ;
int age ;
public Example () {}
public Example ( int age , String name ) {
this . age = age ;
this . name = name ;
}
public String getName () { return name ; }
public int getAge () { return age ; }
}
Achten Sie darauf, keine privaten Felder zu verwenden, wenn Sie die Standardstrategie zur Feldserialisierung verwenden, da dies aufgrund der Reflexion zu Leistungseinbußen führt.
Um den generierten Code zu verwenden, können Sie direkt oder über die Dienstprogrammklasse Parcels
auf die generierte Klasse verweisen:
Parcelable wrapped = Parcels . wrap ( new Example ( "Andy" , 42 ));
Um @Parcel
zu dereferenzieren, rufen Sie einfach die Methode Parcels.unwrap()
auf:
Example example = Parcels . unwrap ( wrapped );
example . getName (); // Andy
example . getAge (); // 42
Natürlich kann das verpackte Parcelable
zu einem Android-Bundle hinzugefügt werden, um es von Aktivität zu Aktivität zu übertragen:
Bundle bundle = new Bundle ();
bundle . putParcelable ( "example" , Parcels . wrap ( example ));
Und in der onCreate()
Methode dereferenziert:
Example example = Parcels . unwrap ( getIntent (). getParcelableExtra ( "example" ));
Diese Ein- und Auspacktechnik passt gut zum Intent Factory-Muster. Darüber hinaus wird Parceler von den folgenden Bibliotheken unterstützt:
Transfuse – Ermöglicht die Verwendung @Parcel
-annotierten Beans mit der @Extra
Injektion.
FragmentArgs – Verwendet den ParcelerArgsBundler
-Adapter, um @Parcel
Parcel-annotierte Beans mit Fragmentparametern zu verpacken und zu entpacken.
Dart – Erkennt mit @Parcel
annotierte Beans automatisch und entpackt sie automatisch, wenn @InjectExtra
verwendet wird.
AndroidAnnotations – Erkennt mit @Parcel
annotierte Beans automatisch und verpackt/entpackt sie automatisch, wenn @Extra
, @FragmentArg
, @InstanceState
und andere Bundle
-bezogene Annotationen verwendet werden.
ActivityStarter – Unterstützt native Parceler-Objekte als Argumente für Aktivitäten, Fragmente, Dienste usw.
Remoter – Unterstützt native Parceler-Objekte als Argumente in @Remoter-Schnittstellen.
Als Attribute einer @Parcel
-Klasse darf nur eine ausgewählte Anzahl von Typen verwendet werden. Die folgende Liste enthält die zugeordneten Typen:
byte
double
float
int
long
char
boolean
String
IBinder
Bundle
SparseArray
eines der zugeordneten Typen*
SparseBooleanArray
ObservableField
List
, ArrayList
und LinkedList
aller zugeordneten Typen*
Map
, HashMap
, LinkedHashMap
, SortedMap
und TreeMap
aller zugeordneten Typen*
Set
, HashSet
, SortedSet
, TreeSet
, LinkedHashSet
eines der zugeordneten Typen*
Parcelable
Serializable
Array aller zugeordneten Typen
Jede andere mit @Parcel
annotierte Klasse
*Parcel führt zu einem Fehler, wenn der generische Parameter nicht zugeordnet ist.
Parceler unterstützt auch alle oben genannten Typen direkt. Dies ist besonders nützlich, wenn Sie mit Klassensammlungen arbeiten, die mit @Parcel
annotiert sind:
Parcelable listParcelable = Parcels . wrap ( new ArrayList < Example >());
Parcelable mapParcelable = Parcels . wrap ( new HashMap < String , Example >());
Beachten Sie, dass Parceler Vererbungshierarchien nicht entpackt, sodass alle polymorphen Felder als Instanzen der Basisklasse entpackt werden. Dies liegt daran, dass Parceler sich für die Leistung entscheidet, anstatt .getClass()
für jedes Datenelement zu überprüfen.
@ Parcel
public class Example {
public Parent p ;
@ ParcelConstructor Example ( Parent p ) { this . p = p ; }
}
@ Parcel public class Parent {}
@ Parcel public class Child extends Parent {}
Example example = new Example ( new Child ());
System . out . println ( "%b" , example . p instanceof Child ); // true
example = Parcels . unwrap ( Parcels . wrap ( example ));
System . out . println ( "%b" , example . p instanceof Child ); // false
Ein Beispiel für die Arbeit mit polymorphen Feldern finden Sie im Abschnitt „Benutzerdefinierte Serialisierung“.
Parceler bietet zusätzlich zur oben gezeigten feldbasierten Serialisierung mehrere Möglichkeiten zum Serialisieren und Deserialisieren eines Objekts.
Parceler kann für die Serialisierung mithilfe von Getter- und Setter-Methoden und einem nicht leeren Konstruktor konfiguriert werden. Darüber hinaus können Felder, Methoden und Konstruktorparameter mithilfe der Annotation @ParcelProperty
verknüpft werden. Dies unterstützt eine Reihe von Bean-Strategien, einschließlich Unveränderlichkeit und traditionelle Getter/Setter-Beans.
Um die Serialisierung der Standardmethode zu konfigurieren, konfigurieren Sie einfach die @Parcel
-Annotation mit Serialization.BEAN
:
@ Parcel ( Serialization . BEAN )
public class Example {
private String name ;
private int age ;
private boolean enabled ;
public String getName () { return name ; }
public void setName ( String name ) { this . name = name ; }
public int getAge () { return age ; }
public void setAge ( int age ) { this . age = age ; }
public boolean isEnabled () { return enabled ; }
public void setEnabled ( boolean enabled ) { this . enabled = enabled ; }
}
Um einen Konstruktor mit Serialisierung zu verwenden, kommentieren Sie den gewünschten Konstruktor mit der Annotation @ParcelConstructor
:
@ Parcel ( Serialization . BEAN )
public class Example {
private final String name ;
private final int age ;
private boolean enabled ;
@ ParcelConstructor
public Example ( int age , String name , boolean enabled ) {
this . age = age ;
this . name = name ;
this . enabled = enabled ;
}
public String getName () { return name ; }
public int getAge () { return age ; }
public boolean isEnabled () { return enabled ; }
}
Wenn ein leerer Konstruktor vorhanden ist, verwendet Parceler diesen Konstruktor, sofern kein anderer Konstruktor mit Anmerkungen versehen ist.
Sie können Serialisierungstechniken auch mit der Annotation @ParcelProperty
kombinieren und anpassen. Im folgenden Beispiel werden firstName
und lastName
mithilfe des Konstruktors in die Bean geschrieben, während firstName
mithilfe des Felds aus der Bean gelesen wird und lastName
mithilfe der Methode getLastName()
gelesen wird. Die Parameter firstName
und lastName
werden jeweils durch die Parameternamen "first"
und "last"
koordiniert.
@ Parcel
public class Example {
@ ParcelProperty ( "first" )
String firstName ;
String lastName ;
@ ParcelConstructor
public Example ( @ ParcelProperty ( "first" ) String firstName , @ ParcelProperty ( "last" ) String lastName ){
this . firstName = firstName ;
this . lastName = lastName ;
}
public String getFirstName () { return firstName ; }
@ ParcelProperty ( "last" )
public String getLastName () { return lastName ; }
}
Für Attribute, die nicht mit Parceler serialisiert werden sollen, können das Attributfeld, der Getter oder Setter mit @Transient
annotiert werden.
Parceler unterstützt viele verschiedene Stile rund um das POJO. Dadurch können @Parcel
annotierte Klassen mit anderen POJO-basierten Bibliotheken verwendet werden, einschließlich der folgenden:
GSON
Reich
Schrank
Einfaches XML
DBFlow
Als Alternative zur direkten Verwendung eines Konstruktors unterstützt Parceler die Verwendung einer annotierten Static Factory zum Erstellen einer Instanz der angegebenen Klasse. Dieser Stil unterstützt den AutoValue-Annotationsprozessor/die Codegenerierungsbibliothek von Google zum Generieren unveränderlicher Beans. Parceler ist über die Annotation @ParcelFactory
mit AutoValue verbunden, die eine statische Factory-Methode der annotierten @Parcel
Serialisierung zuordnet:
@ AutoValue
@ Parcel
public abstract class AutoValueParcel {
@ ParcelProperty ( "value" ) public abstract String value ();
@ ParcelFactory
public static AutoValueParcel create ( String value ) {
return new AutoValue_AutoValueParcel ( value );
}
}
AutoValue generiert eine andere Klasse als das mit Anmerkungen versehene @Parcel
. Daher müssen Sie angeben, welche Klasse Parceler in der Dienstprogrammklasse Parcels
erstellen soll:
Parcelable wrappedAutoValue = Parcels . wrap ( AutoValueParcel . class , AutoValueParcel . create ( "example" ));
Und zum Deserialisieren:
AutoValueParcel autoValueParcel = Parcels . unwrap ( wrappedAutoValue );
@Parcel
enthält einen optionalen Parameter zum Einbinden eines manuellen Serialisierers ParcelConverter
für den Fall, dass eine spezielle Serialisierung erforderlich ist. Dies bietet eine noch sauberere Option für die Verwendung von Parcelable-Klassen als deren manuelle Implementierung.
Der folgende Code demonstriert die Verwendung eines ParcelConverter
zum Entpacken der Vererbungshierarchie während der Deserialisierung.
@ Parcel
public class Item {
@ ParcelPropertyConverter ( ItemListParcelConverter . class )
public List < Item > itemList ;
}
@ Parcel public class SubItem1 extends Item {}
@ Parcel public class SubItem2 extends Item {}
public class ItemListParcelConverter implements ParcelConverter < List < Item >> {
@ Override
public void toParcel ( List < Item > input , Parcel parcel ) {
if ( input == null ) {
parcel . writeInt (- 1 );
}
else {
parcel . writeInt ( input . size ());
for ( Item item : input ) {
parcel . writeParcelable ( Parcels . wrap ( item ), 0 );
}
}
}
@ Override
public List < Item > fromParcel ( Parcel parcel ) {
int size = parcel . readInt ();
if ( size < 0 ) return null ;
List < Item > items = new ArrayList < Item >();
for ( int i = 0 ; i < size ; ++ i ) {
items . add (( Item ) Parcels . unwrap ( parcel . readParcelable ( Item . class . getClassLoader ())));
}
return items ;
}
}
Parceler ist außerdem mit einer Reihe von Basisklassen ausgestattet, die die Sammlungskonvertierung erleichtern und sich im Paket org.parceler.converter
der API befinden. Diese Basisklassen kümmern sich um eine Vielzahl schwieriger oder ausführlicher Aufgaben im Zusammenhang mit Sammlungen, einschließlich Nullprüfungen und Sammeliteration. Der obige ParcelConverter
könnte beispielsweise mit dem „ArrayListParcelConverter“ geschrieben werden:
public class ItemListParcelConverter extends ArrayListParcelConverter < Item > {
@ Override
public void itemToParcel ( Item item , Parcel parcel ) {
parcel . writeParcelable ( Parcels . wrap ( item ), 0 );
}
@ Override
public Item itemFromParcel ( Parcel parcel ) {
return Parcels . unwrap ( parcel . readParcelable ( Item . class . getClassLoader ()));
}
}
Für Klassen, deren entsprechende Java-Quelle nicht verfügbar ist, kann man die Klasse mithilfe der Annotation @ParcelClass
als Parcel einschließen. Diese Anmerkung kann an einer geeigneten Stelle in der kompilierten Quelle deklariert werden. Beispielsweise könnte man die @ParcelClass
zusammen mit der Android-Anwendung einbinden:
@ ParcelClass ( LibraryParcel . class )
public class AndroidApplication extends Application {
//...
}
Mit der Annotation @ParcelClass
können mehrere @ParcelClasses
-Annotationen deklariert werden.
Darüber hinaus können von @ParcelClass
referenzierte Klassen mithilfe der @Parcel
-Annotation konfiguriert werden. Dies ermöglicht die Serialisierungskonfiguration über jeden Parameter, der in der @Parcel
-Annotation verfügbar ist, einschließlich der Serialisierungstechnik oder der zu analysierenden Klassen.
Eine nützliche Technik ist die Möglichkeit, globale benutzerdefinierte Konverter für einen Typ zu definieren:
@ ParcelClass (
value = LibraryParcel . class ,
annotation = @ Parcel ( converter = LibraryParcelConverter . class ))
class SomeClass {}
Dies ermöglicht eine differenzierte Steuerung einer Klasse, die nicht direkt geändert werden kann.
Bei manchen Bibliotheken ist es üblich, eine Bean zum Erweitern einer Basisklasse zu benötigen. Obwohl dies nicht der optimalste Fall ist, unterstützt Parceler diese Vorgehensweise, indem es die Konfiguration ermöglicht, welche Klassen in der Vererbungshierarchie über den Analyseparameter analysiert werden sollen:
@ Parcel ( analyze = { One . class , Three . class })
class One extends Two {}
class Two extends Three {}
class Three extends BaseClass {}
In diesem Beispiel werden nur Felder der Klassen One
und Three
serialisiert, wobei sowohl die Klassenparameter BaseClass
als auch Two
vermieden werden.
Die Dienstprogrammklasse „Parcels“ sucht nach der angegebenen Klasse, um sie nach Klasse zu verpacken. Aus Leistungsgründen wird die Vererbung sowohl der Super- als auch der Basisklasse ignoriert. Für dieses Problem gibt es zwei Lösungen. Zunächst kann man über den implementations
zusätzliche Typen angeben, die dem angegebenen Typ zugeordnet werden sollen:
class ExampleProxy extends Example {}
@ Parcel ( implementations = { ExampleProxy . class })
class Example {}
ExampleProxy proxy = new ExampleProxy ();
Parcels . wrap ( proxy ); // ExampleProxy will be serialized as a Example
Zweitens kann man bei Verwendung der Methode Parcels.wrap()
auch den Klassentyp angeben:
ExampleProxy proxy = new ExampleProxy ();
Parcels . wrap ( Example . class , proxy );
Um Proguard zu konfigurieren, fügen Sie die folgenden Zeilen zu Ihrer Proguard-Konfigurationsdatei hinzu. Diese speichern Dateien, die sich auf die Dienstprogrammklasse Parcels
und die Instanz Parcelable
CREATOR
beziehen: äh
# Paketbibliothek -keep-Schnittstelle org.parceler.Parcel -keep @org.parceler.Parcel class * { *; } -keep class **$$Parcelable { *; }
Sie können Parceler als Maven-Abhängigkeit herunterladen:
< dependency >
< groupId >org.parceler</ groupId >
< artifactId >parceler</ artifactId >
< version >1.1.12</ version >
< scope >provided</ scope >
</ dependency >
< dependency >
< groupId >org.parceler</ groupId >
< artifactId >parceler-api</ artifactId >
< version >1.1.12</ version >
</ dependency >
oder Gradle:
implementation ' org.parceler:parceler-api:1.1.12 '
annotationProcessor ' org.parceler:parceler:1.1.12 '
Oder von Maven Central.
Copyright 2011-2015 John Ericksen Lizenziert unter der Apache-Lizenz, Version 2.0 (die „Lizenz“); Sie dürfen diese Datei nur in Übereinstimmung mit der Lizenz verwenden. Eine Kopie der Lizenz erhalten Sie unter http://www.apache.org/licenses/LICENSE-2.0 Sofern nicht gesetzlich vorgeschrieben oder schriftlich vereinbart, handelt es sich um Software die unter der Lizenz vertrieben werden, werden auf der BASIS „WIE BESEHEN“ verteilt, OHNE GEWÄHRLEISTUNGEN ODER BEDINGUNGEN JEGLICHER ART, weder ausdrücklich noch stillschweigend. Die spezifische Sprache, die die Berechtigungen regelt, finden Sie in der Lizenz Einschränkungen im Rahmen der Lizenz.