有问题吗?在 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
。您只需使用@Parcel
注释 POJO,Parceler 就会完成剩下的工作。由于 Parceler 使用 Java JSR-269 Annotation Processor,因此无需手动运行工具来生成 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 之间传输:
Bundle bundle = new Bundle ();
bundle . putParcelable ( "example" , Parcels . wrap ( example ));
并在onCreate()
方法中取消引用:
Example example = Parcels . unwrap ( getIntent (). getParcelableExtra ( "example" ));
这种包装和展开技术与意图工厂模式配合得很好。此外,Parceler 还受以下库支持:
Transfuse - 允许@Parcel
注释的bean与@Extra
注入一起使用。
FragmentArgs - 使用ParcelerArgsBundler
适配器来包装和解开带有片段参数的@Parcel
注解的 bean。
Dart - 自动检测@Parcel
注解的 beans 并在使用@InjectExtra
时自动解开它们。
AndroidAnnotations - 自动检测@Parcel
注解的 beans,并在使用@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 可以配置为使用 getter 和 setter 方法以及非空构造函数进行序列化。此外,字段、方法和构造函数参数可以使用@ParcelProperty
注释进行关联。这支持许多 bean 策略,包括不变性和传统的 getter/setter bean。
要配置默认方法序列化,只需使用Serialization.BEAN
配置@Parcel
注释:
@ 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
写入bean,同时使用字段从bean 中读取firstName
,并使用getLastName()
方法读取lastName
。参数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 序列化的属性,属性字段、getter 或 setter 可以用@Transient
注释。
Parceler 支持以 POJO 为中心的多种不同样式。这允许@Parcel
注解的类与其他基于 POJO 的库一起使用,包括以下内容:
格森
领域
橱柜
简单的XML
数据库流
作为直接使用构造函数的替代方法,Parceler 支持使用带注释的静态工厂来构建给定类的实例。这种风格支持 Google 的 AutoValue 注释处理器/代码生成库,用于生成不可变的 beans。 Parceler 通过@ParcelFactory
注解与 AutoValue 接口,该注解将静态工厂方法映射到带注解的@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还封装了一系列基类,以便更方便地进行Collection转换,位于api的org.parceler.converter
包下。这些基类负责处理集合的各种困难或冗长的工作,包括空检查和收集迭代。例如,上面的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 源的类,可以使用@ParcelClass
注释将该类包含为 Parcel。该注释可以在编译源中任何方便的地方声明。例如,可以将@ParcelClass
与 Android 应用程序一起包含:
@ ParcelClass ( LibraryParcel . class )
public class AndroidApplication extends Application {
//...
}
可以使用@ParcelClasses
注释来声明多个@ParcelClass
注释。
此外, @ParcelClass
引用的类可以使用@Parcel
注释进行配置。这允许通过@Parcel
注释上可用的任何参数进行序列化配置,包括要分析的序列化技术或类。
一种有用的技术是能够为类型定义全局自定义转换器:
@ ParcelClass (
value = LibraryParcel . class ,
annotation = @ Parcel ( converter = LibraryParcelConverter . class ))
class SomeClass {}
这允许对无法直接修改的类进行细粒度控制。
对于某些库来说,需要 bean 来扩展基类是一种常见的做法。虽然这不是最理想的情况,但 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
实例相关的文件:呃
# 打包器库 -保留接口org.parceler.Parcel -keep @org.parceler.Parcel 类 * { *; } -保持类 **$$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 除非适用法律要求或书面同意,否则软件 根据许可证分发是在“按原样”基础上分发的, 不提供任何明示或暗示的保证或条件。 请参阅许可证以了解特定语言的管理权限和 许可证下的限制。