Parceler

標(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能配置序列化使用的gettersetternon-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è)例子汽纤,只有OneThree會(huì)被序列化上岗,避開了TwoBaseClass的參數(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匿垄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌椿疗,老刑警劉巖漏峰,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異变丧,居然都是意外死亡芽狗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門痒蓬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人滴劲,你說(shuō)我怎么就攤上這事攻晒。” “怎么了班挖?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵鲁捏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我萧芙,道長(zhǎng)给梅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任双揪,我火速辦了婚禮动羽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渔期。我一直安慰自己运吓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布疯趟。 她就那樣靜靜地躺著拘哨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪信峻。 梳的紋絲不亂的頭發(fā)上倦青,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音盹舞,去河邊找鬼产镐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛矾策,可吹牛的內(nèi)容都是我干的磷账。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贾虽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼逃糟!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绰咽,失蹤者是張志新(化名)和其女友劉穎菇肃,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體取募,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琐谤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玩敏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斗忌。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旺聚,靈堂內(nèi)的尸體忽然破棺而出织阳,到底是詐尸還是另有隱情,我是刑警寧澤砰粹,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布唧躲,位于F島的核電站,受9級(jí)特大地震影響碱璃,放射性物質(zhì)發(fā)生泄漏弄痹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一嵌器、第九天 我趴在偏房一處隱蔽的房頂上張望肛真。 院中可真熱鬧,春花似錦嘴秸、人聲如沸毁欣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凭疮。三九已至,卻和暖如春串述,著一層夾襖步出監(jiān)牢的瞬間执解,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工纲酗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留衰腌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓觅赊,卻偏偏與公主長(zhǎng)得像右蕊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吮螺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android跨進(jìn)程通信IPC整體內(nèi)容如下 1饶囚、Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)2帕翻、Andro...
    隔壁老李頭閱讀 11,847評(píng)論 6 38
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評(píng)論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)萝风,斷路器嘀掸,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 古典老師提出的一個(gè)觀點(diǎn)是睬塌,好的心態(tài)其實(shí)是一個(gè)良好的心理模式,人與人之間的智商不會(huì)相差很多歇万,只是心智運(yùn)營(yíng)模式不同罷了...
    Sunny瑩閱讀 683評(píng)論 0 1
  • 《殺戮與文化》一書中第一章中講到了小居魯士雇傭希臘人做雇傭兵揩晴,幫助其復(fù)國(guó),希臘雇傭兵傷亡慘重贪磺,他們還是堅(jiān)持...
    從彥閱讀 2,013評(píng)論 0 2