Есть вопросы? Спросите об этом на StackOverflow.
Нашли проблему? Пожалуйста, сообщите об этом.
В Android Parcelables — отличный способ сериализации объектов Java между контекстами. По сравнению с традиционной сериализацией, Parcelables требует в 10 раз меньше времени как для сериализации, так и для десериализации. Однако у Parcelables есть серьезный недостаток. Parcelables содержат массу шаблонного кода. Чтобы реализовать Parcelable, вы должны зеркально отобразить методы writeToParcel()
и createFromParcel()
, чтобы они читали и записывали в Parcel в одном и том же порядке. Кроме того, Parcelable должен определить public static final Parcelable.Creator CREATOR
, чтобы инфраструктура Android могла использовать код сериализации.
Parceler — это библиотека генерации кода, которая генерирует исходный исходный код Android Parcelable. Вам больше не нужно реализовывать интерфейс Parcelable, writeToParcel()
или createFromParcel()
или public static final CREATOR
. Вы просто аннотируете POJO с помощью @Parcel
, а Parceler делает все остальное. Поскольку Parceler использует процессор аннотаций Java JSR-269, нет необходимости запускать инструмент вручную для создания кода Parcelable. Просто аннотируйте свой Java Bean, скомпилируйте, и все готово. По умолчанию Parceler будет сериализовать поля вашего экземпляра напрямую:
@ 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 ; }
}
Будьте осторожны и не используйте частные поля при использовании стратегии сериализации полей по умолчанию, поскольку это приведет к снижению производительности из-за отражения.
Чтобы использовать сгенерированный код, вы можете напрямую ссылаться на сгенерированный класс или через служебный класс Parcels
:
Parcelable wrapped = Parcels . wrap ( new Example ( "Andy" , 42 ));
Чтобы разыменовать @Parcel
, просто вызовите метод Parcels.unwrap()
:
Example example = Parcels . unwrap ( wrapped );
example . getName (); // Andy
example . getAge (); // 42
Конечно, завернутый Parcelable
можно добавить в Android Bundle для передачи из Activity в Activity:
Bundle bundle = new Bundle ();
bundle . putParcelable ( "example" , Parcels . wrap ( example ));
И разыменовывается в методе onCreate()
:
Example example = Parcels . unwrap ( getIntent (). getParcelableExtra ( "example" ));
Эта техника переноса и развертывания хорошо сочетается с шаблоном Intent Factory. Кроме того, Parceler поддерживается следующими библиотеками:
Transfuse — позволяет использовать bean-компоненты с аннотациями @Parcel
вместе с инъекцией @Extra
.
FragmentArgs — использует адаптер ParcelerArgsBundler
для переноса и развертывания аннотированных bean-компонентов @Parcel
с параметрами фрагмента.
Dart — автоматически определяет @Parcel
аннотированные bean-компоненты и автоматически разворачивает их при использовании @InjectExtra
.
AndroidAnnotations — автоматически определяет аннотированные bean-компоненты @Parcel
и автоматически оборачивает/разворачивает их при использовании @Extra
, @FragmentArg
, @InstanceState
и других аннотаций, связанных с Bundle
.
ActivityStarter — изначально поддерживает объекты Parceler в качестве аргументов для действий, фрагментов, служб и т. д.
Remoter — изначально поддерживает объекты Parceler в качестве аргументов в интерфейсах @Remoter.
В качестве атрибутов класса @Parcel
можно использовать только избранное количество типов. Следующий список включает сопоставленные типы:
byte
double
float
int
long
char
boolean
String
IBinder
Bundle
SparseArray
любого из сопоставленных типов*
SparseBooleanArray
ObservableField
List
, ArrayList
и LinkedList
любого из сопоставленных типов*
Map
, HashMap
, LinkedHashMap
, SortedMap
и TreeMap
любого из сопоставленных типов*
Set
, HashSet
, SortedSet
, TreeSet
, LinkedHashSet
любого из сопоставленных типов*
Parcelable
Serializable
Массив любого из сопоставленных типов
Любой другой класс, помеченный @Parcel
*Parcel выдаст ошибку, если общий параметр не сопоставлен.
Parceler также напрямую поддерживает любой из вышеперечисленных типов. Это особенно полезно при работе с коллекциями классов, аннотированных @Parcel
:
Parcelable listParcelable = Parcels . wrap ( new ArrayList < Example >());
Parcelable mapParcelable = Parcels . wrap ( new HashMap < String , Example >());
Обратите внимание, что Parceler не разворачивает иерархии наследования, поэтому любые полиморфные поля будут развернуты как экземпляры базового класса. Это связано с тем, что Parceler выбирает производительность, а не проверку .getClass()
для каждого фрагмента данных.
@ 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
В разделе «Пользовательская сериализация» приведен пример работы с полиморфными полями.
Parceler предлагает несколько вариантов сериализации и десериализации объекта в дополнение к сериализации на основе полей, показанной выше.
Parceler можно настроить для сериализации с использованием методов получения и установки, а также непустого конструктора. Кроме того, поля, методы и параметры конструктора могут быть связаны с помощью аннотации @ParcelProperty
. Это поддерживает ряд стратегий bean-компонентов, включая неизменяемость и традиционные bean-компоненты получения/установки.
Чтобы настроить сериализацию метода по умолчанию, просто настройте аннотацию @Parcel
с помощью 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 ; }
}
Чтобы использовать конструктор с сериализацией, добавьте к нужному конструктору аннотацию @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 ; }
}
Если присутствует пустой конструктор, Parceler будет использовать этот конструктор, если не указан другой конструктор.
Вы также можете комбинировать методы сериализации, используя аннотацию @ParcelProperty
. В следующем примере firstName
и lastName
записываются в компонент с помощью конструктора, firstName
считывается из компонента с помощью поля, а lastName
считывается с помощью метода getLastName()
. Параметры firstName
и lastName
координируются именами параметров "first"
и "last"
соответственно.
@ 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 ; }
}
Для атрибутов, которые не следует сериализовать с помощью Parceler, поле атрибута, метод получения или установки могут быть аннотированы @Transient
.
Parceler поддерживает множество различных стилей, основанных на POJO. Это позволяет использовать аннотированные классы @Parcel
с другими библиотеками на основе POJO, включая следующие:
ГСОН
Область
Буфет
Простой XML
ДБФлоу
В качестве альтернативы непосредственному использованию конструктора Parceler поддерживает использование аннотированной статической фабрики для создания экземпляра данного класса. Этот стиль поддерживает процессор аннотаций/библиотеку генерации кода Google AutoValue для создания неизменяемых bean-компонентов. Parceler взаимодействует с AutoValue через аннотацию @ParcelFactory
, которая сопоставляет статический фабричный метод с аннотированной сериализацией @Parcel
:
@ 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 генерирует класс, отличный от аннотированного @Parcel
, поэтому вам необходимо указать, какой класс Parceler должен построить в служебном классе Parcels
:
Parcelable wrappedAutoValue = Parcels . wrap ( AutoValueParcel . class , AutoValueParcel . create ( "example" ));
И для десериализации:
AutoValueParcel autoValueParcel = Parcels . unwrap ( wrappedAutoValue );
@Parcel
включает необязательный параметр для включения ручного сериализатора ParcelConverter
на случай, если необходима специальная сериализация. Это обеспечивает еще более чистый вариант использования классов Parcelable, чем их реализация вручную.
Следующий код демонстрирует использование ParcelConverter
для развертывания иерархии наследования во время десериализации.
@ 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 также поставляется с рядом базовых классов, упрощающих преобразование коллекций, которые расположены в пакете org.parceler.converter
API. Эти базовые классы выполняют множество сложных или многословных задач, связанных с коллекциями, включая проверки на нулевое значение и итерацию сбора. Например, приведенный выше ParcelConverter
можно написать с использованием ArrayListParcelConverter:
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 ()));
}
}
Для классов, соответствующий исходный код Java недоступен, можно включить класс как Parcel, используя аннотацию @ParcelClass
. Эту аннотацию можно объявить в любом удобном месте скомпилированного исходного кода. Например, можно включить @ParcelClass
вместе с приложением Android:
@ ParcelClass ( LibraryParcel . class )
public class AndroidApplication extends Application {
//...
}
С помощью аннотации @ParcelClass
можно объявить несколько аннотаций @ParcelClasses
.
Кроме того, классы, на которые ссылается @ParcelClass
можно настроить с помощью аннотации @Parcel
. Это позволяет настраивать сериализацию с помощью любого параметра, доступного в аннотации @Parcel
, включая метод сериализации или классы для анализа.
Одним из полезных методов является возможность определять глобальные пользовательские преобразователи для типа:
@ ParcelClass (
value = LibraryParcel . class ,
annotation = @ Parcel ( converter = LibraryParcelConverter . class ))
class SomeClass {}
Это обеспечивает детальный контроль над классом, который недоступен для прямого изменения.
Некоторые библиотеки часто требуют, чтобы компонент расширял базовый класс. Хотя это не самый оптимальный случай, Parceler поддерживает эту практику, позволяя настраивать, какие классы в иерархии наследования анализировать, с помощью параметра анализа:
@ Parcel ( analyze = { One . class , Three . class })
class One extends Two {}
class Two extends Three {}
class Three extends BaseClass {}
В этом примере будут сериализованы только поля классов One
и Three
, без параметров классов BaseClass
и Two
.
Утилитный класс Parcels ищет данный класс для переноса по классу. По соображениям производительности это игнорирует наследование как суперклассов, так и базовых классов. Есть два решения этой проблемы. Во-первых, можно указать дополнительные типы, которые будут связаны с данным типом, через параметр implementations
:
class ExampleProxy extends Example {}
@ Parcel ( implementations = { ExampleProxy . class })
class Example {}
ExampleProxy proxy = new ExampleProxy ();
Parcels . wrap ( proxy ); // ExampleProxy will be serialized as a Example
Во-вторых, при использовании метода Parcels.wrap()
также можно указать тип класса:
ExampleProxy proxy = new ExampleProxy ();
Parcels . wrap ( Example . class , proxy );
Чтобы настроить Proguard, добавьте следующие строки в файл конфигурации proguard. В них будут храниться файлы, связанные с служебным классом Parcels
и экземпляром Parcelable
CREATOR
: er
# Библиотека посылок -сохранить интерфейс org.parceler.Parcel -keep @org.parceler.Parcel class * { *; } -keep class **$$Parcelable { *; }
Вы можете скачать Parceler как зависимость Maven:
< 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 >
или Градл:
implementation ' org.parceler:parceler-api:1.1.12 '
annotationProcessor ' org.parceler:parceler:1.1.12 '
Или из Maven Central.
Авторские права: Джон Эриксен, 2011–2015 гг. Лицензируется по лицензии Apache версии 2.0 («Лицензия»); вы не можете использовать этот файл, кроме как в соответствии с Лицензией. Вы можете получить копию Лицензии по адресу: http://www.apache.org/licenses/LICENSE-2.0 Если это не требуется действующим законодательством или не согласовано в письменной форме, программное обеспечение распространяется по Лицензии, распространяется на условиях «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ ИЛИ УСЛОВИЙ, явных или подразумеваемых. См. Лицензию для конкретного языка, регулирующего разрешения и ограничения по Лицензии.