標(biāo)簽(空格分隔): Android Serialize 翻譯 code_generator
Android的Parcelable代碼生成器 原碼
[TOC]
介紹
在Android 中榴芳,Parcelables是在Contexts之前傳遞數(shù)據(jù)非常棒的類序列化方式算柳。和傳統(tǒng)的Serialization相比(兩者之前的差異)痪宰,parcelable在進(jìn)行序列化和反序列化時(shí)所需要的時(shí)間是傳統(tǒng)序列化的十分之一。然而Parcelables有一個(gè)嚴(yán)重的瑕疵就是包含大量模板代碼。實(shí)現(xiàn)一個(gè)Parceable,必需實(shí)現(xiàn)writeToParcel()
和 createFromParcel()
方法,按同樣的順序讀和寫轉(zhuǎn)化成Parcel
厕吉。而且,為了使能獲取拆包的類型械念, Parcelable 必需定義public final static Parcelable.Creator CREATOR<T>
變量,明確T的類型头朱。更多關(guān)于Pacelable接口參數(shù)Parcelable接口的使用。
Parceler 是一個(gè)代碼代碼生成器包龄减,用于生成Android Parcelable 模板代碼项钮。你不再需要實(shí)現(xiàn)Parcelable接口,實(shí)現(xiàn) writeToParcel() 希停、 createFromParcel() 方法烁巫,定義 the public static final CREATOR變量。只需要使用@Parcel
注解POJO類宠能,Parcelerw會(huì)做上面的工作亚隙。Parceler使用the Java JSR-269 Annotation Processor
,所以我們不再需要手動(dòng)運(yùn)行工具去生成Parcelable代碼。僅僅注解Java Bean,編譯完成违崇,默認(rèn)(可配置)情況下Parceler會(huì)直接序列化Java Bean域.
@Parcel
public class Example {
String name;
int age;
public Example(){ /*Required empty bean constructor*/ }
public Example(int age, String name) {
this.age = age;
this.name = name;
}
public String getName() { return name; }
public int getAge() { return age; }
}
注意:使用默認(rèn)的序列策略阿弃,不能定義private 變量,因?yàn)樗鼤?huì)產(chǎn)生反射的性能損失羞延。(定義成private也是能編譯和運(yùn)行的)
能直接引用生成的類Example$Parcelable
渣淳,或者通過(guò)Parcels
工具類:
Parcelable wrapped = Parcels.wrap(new Example("Andy", 42));
Example example = Parcels.unwrap(wrapped);
example.getName(); // Andy
example.getAge(); // 42
當(dāng)然,打包了的Parcelable
能直接加入到Android Bundle中在不同Activity之前傳遞伴箩。
Bundle bundle = new Bundle();
bundle.putParcelable("example", Parcels.wrap(example));
Example example = Parcels.unwrap(getIntent().getParcelableExtra("example"));
打包和拆包技術(shù)是工廠方式很好實(shí)現(xiàn)水由。此外,Parceler被以下libraries支持。
-
Transfuse
- Allows @Parcel annotated beans to be used with the @Extra injection. -
FragmentArgs
- Uses the ParcelerArgsBundler adapter to wrap and unwrap @Parcel annotated beans with fragment parameters. -
Dart
- Autodetects @Parcel annotated beans and automatically unwraps them when using @InjectExtra.
Parcel支持的屬性類型
僅有一部分類型能用于注解的@Parcel
類的屬性砂客,以下列表包括能映射的類型:
- byte
- double
- float
- int
- long
- char
- boolean
- String
- IBinder
- Bundle
- SparseArray of any of the mapped types*
- SparseBooleanArray
- List, ArrayList and LinkedList of any of the mapped types*
- Map, HashMap, LinkedHashMap, SortedMap, and TreeMap of any of the mapped types*
- Set, HashSet, SortedSet, TreeSet, LinkedHashSet of any of the mapped types*
- Parcelable
- Serializable
- Array of any of the mapped types
- Any other class annotated with @Parcel
如果泛型參數(shù)不能被映射Parcel會(huì)報(bào)錯(cuò)
Parceler 直接支持上面的所有類型,這非常方便在處理集合類:
Parcelable listParcelable = Parcels.wrap(new ArrayList<Example>());
Parcelable mapParcelable = Parcels.wrap(new HashMap<String, Example>());
多態(tài)
注意Parceler不能拆包繼承關(guān)系呵恢,所以任何多態(tài)域都會(huì)被解壓成基礎(chǔ)類鞠值,之所以這樣是因?yàn)镻arceler 選擇了性能,并沒(méi)有校驗(yàn)每份數(shù)據(jù).getClass()
渗钉。
@Parcel
public class Example {
public Parent p;
//必需添加@ParcelConstructor注解彤恶,否則會(huì)因?yàn)檎也坏侥J(rèn)構(gòu)造函數(shù)報(bào)錯(cuò)
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(example.p instanceof Child); // true
example = Parcels.unwrap(Parcels.wrap(example));
System.out.println(example.p instanceof Child); // false
/* 說(shuō)明一下,上面測(cè)試代碼如果在同一個(gè)context里面則兩個(gè)都是true,看Parcels.unwrap就知道鳄橘,他是直接獲取的之前的對(duì)象声离,中間并沒(méi)有打包和拆包的過(guò)程。*/
參考自定義序列化部分瘫怜,實(shí)現(xiàn)多態(tài)域术徊。
序列化策略
Parceler 除了上面以基本域的序列化方法外,還提供幾種選擇鲸湃,如何去序列化和反序列化一個(gè)對(duì)象赠涮。
Getter/setter serialization
Parceler能配置序列化使用的getter
、setter
和non-empty constructor
.另外暗挑,field
可通過(guò)@ParcelProperty
注解與方法和構(gòu)造函數(shù)的參數(shù)關(guān)聯(lián)笋除,這將支持很多bean
,包括不可變的bean
配置使用getter/settter方法序列化炸裆,只需簡(jiǎn)單給@Parcel
注解設(shè)置其value值為Serialization.BEAN
@Parcel(Serialization.BEAN)
public class Example {
private String name;
private int age;
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; }
}
使用帶參構(gòu)造函數(shù)序列化垃它,必需在帶參函數(shù)上添加 @ParcelConstructor注解:
@Parcel(Serialization.BEAN)
public class Example {
private final String name;
private final int age;
@ParcelConstructor
public Example(int age, String name) {
this.age = age;
this.name = name;
}
public String getName() { return name; }
public int getAge() { return age; }
}
默認(rèn)會(huì)使用無(wú)參構(gòu)造函數(shù),除非帶參構(gòu)造函數(shù)有注解烹看。如果沒(méi)有無(wú)參構(gòu)造函數(shù)且沒(méi)@ParentConstructor注解的帶參構(gòu)造函數(shù)国拇,編譯無(wú)法通過(guò)。
getters/setters and fields混合使用
你能混合使用序列化策略听系,使用@ParcelProperty
贝奇。下面的例子中firstName和lastName使用構(gòu)造函數(shù)打包,然而拆包fistName通過(guò)引用直接讀取靠胜,而lastName則通過(guò)getLastName()方法讀取掉瞳。first、last作為@ParcelProperty()
標(biāo)識(shí)浪漠,必需成對(duì)出現(xiàn) 陕习。
@Parcel
public class Example {
//關(guān)聯(lián)使用該注解的屬性
@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; }
}
不需要序列化的屬性能在其getter/settter方法上注解@Transient,另外帶transient關(guān)鍵字的域也不會(huì)被序列化址愿。
Parceler 支持不同種類的POJO,允許@Parcel
注解的類用于其它處理POJO的包该镣,包括以下
靜態(tài)工廠支持
直接使用構(gòu)造函數(shù)是可選的,Parceler支持使用注解靜態(tài)工廠創(chuàng)建給定類的實(shí)例响谓。這種實(shí)現(xiàn)支持Google的AutoValue注解處理器生成不可變beans
. 實(shí)現(xiàn)了AutoValue的抽象類损合,通過(guò) @ParcelFactory
注解工廠方法省艳,映射成有@Parcel
的類。
使用@AutoValue可能會(huì)報(bào)空指針異常嫁审,問(wèn)題可參考https://github.com/google/auto/issues/240
@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 能生成同的類跋炕,因此告訴Parcles
工具類應(yīng)該實(shí)例化哪個(gè)類:
Parcelable wrappedAutoValue = Parcels.wrap(AutoValueParcel.class, AutoValueParcel.create("example"));
反序列化
AutoValueParcel autoValueParcel = Parcels.unwrap(wrappedAutoValue);
自定義序列化
@Parcel可通過(guò)@ParcelPropertyConverter(ParcelConverter)去指定任意一個(gè)field
序列化過(guò)程.
下面例子演示了使用ParcelConverter反序化有繼承關(guān)系的類
@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在api的org.parcler.converter
包下實(shí)現(xiàn)一系列對(duì)集體的轉(zhuǎn)換,這些類處理了一些判空和迭代器律适,上面的ParcelConverter
可以簡(jiǎn)單的實(shí)現(xiàn):
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()));
}
}
沒(méi)有source 的 classes
對(duì)于那些沒(méi)有Java source 的classses ,也能 通過(guò)使用@ParcelClass注解進(jìn)行序列化辐烂。這個(gè)注解能方便有用于可編譯資源的任何地方。例如捂贿,在Android Appliction 中可對(duì)LibarayParcel.class 序列化:
@ParcelClass(LibraryParcel.class)
public class AndroidApplication extends Application{
//...
}
序列化多個(gè)類時(shí)使用@ParcelClasses
注解
另外纠修,@ParcelClass
關(guān)聯(lián)的類可以annotation屬性配置其將要使用的@Parcel注解的參數(shù),這樣我們就能配置關(guān)聯(lián)類的序列化策略厂僧,以及哪些類需要分析扣草。
一個(gè)有用的策略,規(guī)定一個(gè)類型使用自定的converters.
@ParcelClass(
value = LibraryParcel.class,
annotation = @Parcel(converter = LibraryParcelConverter.class))
class SomeClass{}
這將在class的基礎(chǔ)上進(jìn)行控制吁系,所以不能再動(dòng)態(tài)更改德召。
高級(jí)配置
跳過(guò)分析
libraries中可能需要bean去擴(kuò)展一個(gè)base class,并要求基類中的數(shù)據(jù)不需要打包。Parceler可通過(guò)analysis
配置該類中的哪基類類數(shù)據(jù)打包:
@Parcel(analyze = {One.class, Three.class})
class One extends Two {}
class Two extends Three {}
class Three extends BaseClass {}
這個(gè)例子汽纤,只有One
和Three
會(huì)被序列化上岗,避開了Two
和BaseClass
的參數(shù)
定義打包過(guò)程
Parcels工具類分類查找所有需要打包的類,由于性能優(yōu)化的原因它直接忽略了class的繼承關(guān)系,無(wú)法對(duì)@Parcel的bean的子類進(jìn)行打包.有兩種方法可解決這個(gè)問(wèn)題:
第一種,通過(guò)implementations參數(shù)去關(guān)系他的子類:
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()方法時(shí)肴掷,指定要打包的類(已注解@Parcel,并且是繼承關(guān)系)
ExampleProxy proxy = new ExampleProxy();
Parcels.wrap(Example.class, proxy);
避免indexing錯(cuò)誤
在Libraries
中使用Parceler
將會(huì)面臨挑戰(zhàn)背传,因?yàn)镻arceler生成唯一個(gè)映射類Parceler$$Parcels
去關(guān)聯(lián)一個(gè)bean
和bean所對(duì)應(yīng)的Parcelable
呆瞻,編譯時(shí)這個(gè)映射類可能沖突,并會(huì)拋出下面的錯(cuò)誤:
Error Code:
2
Output:
UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Lorg/parceler/Parceler$$Parcels$1;
at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:594)
at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:552)
at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:533)
....
為了避免去這個(gè)公共的映射類径玖,設(shè)置parcelsIndex = false
到每一個(gè)使用@Parcel注解的類痴脾,Parceler將不會(huì)生成Parceler$$Parcels
映射類,若沒(méi)有沒(méi)映射做為索引梳星,Parceler會(huì)退回去根據(jù)類名查找生成的class
赞赖。
或者,在頂層項(xiàng)目使用@ParcelClass
配置冤灾,而不需要在每@Parcel
注解上進(jìn)行配置前域。
配置混淆
對(duì)于使用了代碼混淆,添加以下幾行到你的混淆配置文件里韵吨。這將保留Parcels
相關(guān)工具類以及Parcelable CARETOR
[靜態(tài)工廠]實(shí)例
# Parcel library
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keep class org.parceler.Parceler$$Parcels
引入Parceler
Maven
<dependency>
<groupId>org.parceler</groupId>
<artifactId>parceler</artifactId>
<version>1.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.parceler</groupId>
<artifactId>parceler-api</artifactId>
<version>1.0.4</version>
</dependency>
Gradle
compile "org.parceler:parceler-api:1.0.4"
apt "org.parceler:parceler:1.0.4"
詳細(xì)使用請(qǐng)參考android-apt project