有問題嗎?在 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 除非適用法律要求或書面同意,否則軟體 根據許可證分發是在“按原樣”基礎上分發的, 不提供任何明示或暗示的保證或條件。 請參閱許可證以了解特定語言的管理權限和 許可證下的限制。