มีคำถาม? ถามได้ที่ 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 Annotation Process จึงไม่จำเป็นต้องรันเครื่องมือด้วยตนเองเพื่อสร้างโค้ด 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 เพื่อถ่ายโอนจากกิจกรรมหนึ่งไปยังอีกกิจกรรมหนึ่งได้:
Bundle bundle = new Bundle ();
bundle . putParcelable ( "example" , Parcels . wrap ( example ));
และยกเลิกการอ้างอิงในเมธอด onCreate()
:
Example example = Parcels . unwrap ( getIntent (). getParcelableExtra ( "example" ));
เทคนิคการห่อและแกะนี้เข้ากันได้ดีกับรูปแบบ Intent Factory นอกจากนี้ Parceler ยังได้รับการสนับสนุนจากไลบรารีต่อไปนี้:
Transfuse - อนุญาตให้ใช้ถั่วที่มีคำอธิบายประกอบ @Parcel
กับการแทรก @Extra
FragmentArgs - ใช้อะแดปเตอร์ ParcelerArgsBundler
เพื่อตัดและแกะ @Parcel
annotated beans ด้วยพารามิเตอร์แฟรกเมนต์
Dart - ตรวจจับถั่วที่มีคำอธิบายประกอบ @Parcel
โดยอัตโนมัติ และแกะถั่วเหล่านั้นโดยอัตโนมัติเมื่อใช้ @InjectExtra
AndroidAnnotations - ตรวจจับถั่วที่มีคำอธิบายประกอบ @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
*พัสดุจะเกิดข้อผิดพลาดหากพารามิเตอร์ทั่วไปไม่ได้รับการแมป
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 แบบดั้งเดิม
หากต้องการกำหนดค่าการทำให้เป็นอนุกรมของวิธีการเริ่มต้น เพียงกำหนดค่าคำอธิบายประกอบ @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
จะถูกเขียนลงใน bean โดยใช้ตัวสร้าง ในขณะที่ firstName
ถูกอ่านจาก bean โดยใช้ฟิลด์ และ 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 ฟิลด์แอ็ตทริบิวต์ getter หรือ setter อาจมีคำอธิบายประกอบโดย @Transient
Parceler รองรับสไตล์ที่แตกต่างกันมากมายโดยมีศูนย์กลางอยู่ที่ POJO ซึ่งช่วยให้สามารถใช้คลาสที่มีคำอธิบายประกอบ @Parcel
กับไลบรารีที่ใช้ POJO อื่นๆ ได้ รวมถึงรายการต่อไปนี้:
จีซอน
อาณาจักร
ตู้
XML อย่างง่าย
ดีบีโฟลว์
อีกทางเลือกหนึ่งนอกเหนือจากการใช้ Constructor โดยตรง Parceler รองรับการใช้ Static Factory ที่มีคำอธิบายประกอบเพื่อสร้างอินสแตนซ์ของคลาสที่กำหนด สไตล์นี้รองรับตัวประมวลผลคำอธิบายประกอบ AutoValue ของ Google / ไลบรารีการสร้างโค้ดสำหรับสร้าง 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 {}
ซึ่งช่วยให้สามารถควบคุมคลาสที่ไม่สามารถแก้ไขได้โดยตรงอย่างละเอียด
เป็นเรื่องปกติสำหรับห้องสมุดบางแห่งที่ต้องการ 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 '
หรือจากมาเวนเซ็นทรัล
Copyright 2011-2015 John Ericksen ได้รับอนุญาตภายใต้ Apache License เวอร์ชัน 2.0 ("ใบอนุญาต"); คุณไม่สามารถใช้ไฟล์นี้ได้เว้นแต่จะเป็นไปตามใบอนุญาต คุณสามารถขอรับสำเนาใบอนุญาตได้ที่ http://www.apache.org/licenses/LICENSE-2.0 เว้นแต่กฎหมายที่ใช้บังคับกำหนดไว้หรือตกลงเป็นลายลักษณ์อักษร ซอฟต์แวร์ เผยแพร่ภายใต้ใบอนุญาตนั้นเผยแพร่ตาม "ตามสภาพ" โดยไม่มีการรับประกันหรือเงื่อนไขใด ๆ ทั้งโดยชัดแจ้งหรือโดยนัย ดูใบอนุญาตสำหรับภาษาเฉพาะที่ควบคุมการอนุญาตและ ข้อจำกัดภายใต้ใบอนุญาต