ปลั๊กอิน kotlin-parcelize
มีตัวสร้างการติดตั้งใช้งาน Parcelable
หากต้องการรวมการรองรับ Parcelable ให้เพิ่มปลั๊กอิน Gradle ลงในไฟล์ build.gradle ของแอป
ดึงดูด
plugins { id 'kotlin-parcelize' }
Kotlin
plugins { id("kotlin-parcelize") }
เมื่อใส่คำอธิบายประกอบในคลาสด้วย @Parcelize ระบบจะสร้างParcelableการติดตั้งใช้งาน
โดยอัตโนมัติ ดังตัวอย่างต่อไปนี้
// import kotlinx.parcelize.Parcelize @Parcelize class User(val firstName: String, val lastName: String, val age: Int) : Parcelable
@Parcelize กำหนดให้ประกาศพร็อพเพอร์ตี้ที่ทำให้เป็นอนุกรมทั้งหมดใน
เครื่องมือสร้างหลัก ปลั๊กอินจะออกคำเตือนในแต่ละพร็อพเพอร์ตี้
ที่มีฟิลด์สำรองที่ประกาศในส่วนเนื้อหาของคลาส นอกจากนี้ คุณยังใช้ @Parcelize ไม่ได้หากพารามิเตอร์ของตัวสร้างหลักบางรายการไม่ใช่พร็อพเพอร์ตี้
หากชั้นเรียนของคุณต้องใช้ตรรกะการทำให้เป็นอนุกรมขั้นสูง ให้เขียนไว้ในคลาสคู่
@Parcelize data class User(val firstName: String, val lastName: String, val age: Int) : Parcelable { private companion object : Parceler<User> { override fun User.write(parcel: Parcel, flags: Int) { // Custom write implementation } override fun create(parcel: Parcel): User { // Custom read implementation } } }
ประเภทที่รองรับ
@Parcelize รองรับประเภทต่างๆ มากมาย
- ประเภทดั้งเดิม (และเวอร์ชันที่อยู่ในกรอบ)
- ออบเจ็กต์และ Enum
String,CharSequenceDurationExceptionSize,SizeF,Bundle,IBinder,IInterface,FileDescriptorSparseArray,SparseIntArray,SparseLongArray,SparseBooleanArray- การติดตั้งใช้งาน
Serializableทั้งหมด (รวมถึงDate) และParcelable - คอลเล็กชันของประเภทที่รองรับทั้งหมด:
List(แมปกับArrayList),Set(แมปกับLinkedHashSet),Map(แมปกับLinkedHashMap)- นอกจากนี้ ยังมีการใช้งานจริงอีกหลายอย่าง เช่น
ArrayList,LinkedList,SortedSet,NavigableSet,HashSet,LinkedHashSet,TreeSet,SortedMap,NavigableMap,HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap
- นอกจากนี้ ยังมีการใช้งานจริงอีกหลายอย่าง เช่น
- อาร์เรย์ของประเภทที่รองรับทั้งหมด
- เวอร์ชันที่อนุญาตให้เป็นค่าว่างของประเภทที่รองรับทั้งหมด
Parceler ที่กำหนดเอง
หากระบบไม่รองรับประเภทของคุณโดยตรง คุณสามารถเขียนParceler
ออบเจ็กต์การแมปสำหรับประเภทนั้นได้
class ExternalClass(val value: Int) object ExternalClassParceler : Parceler<ExternalClass> { override fun create(parcel: Parcel) = ExternalClass(parcel.readInt()) override fun ExternalClass.write(parcel: Parcel, flags: Int) { parcel.writeInt(value) } }
คุณใช้เครื่องมือแยกวิเคราะห์ภายนอกได้โดยใช้คำอธิบายประกอบ @TypeParceler หรือ @WriteWith
// Class-local parceler @Parcelize @TypeParceler<ExternalClass, ExternalClassParceler>() class MyClass(val external: ExternalClass) : Parcelable
// Property-local parceler @Parcelize class MyClass(@TypeParceler<ExternalClass, ExternalClassParceler>() val external: ExternalClass) : Parcelable
// Type-local parceler @Parcelize class MyClass(val external: @WriteWith<ExternalClassParceler>() ExternalClass) : Parcelable
สร้างข้อมูลจาก Parcel
ในโค้ด Java คุณสามารถเข้าถึงฟิลด์ CREATOR ได้โดยตรง
class UserCreator {
static User fromParcel(Parcel parcel) {
return User.CREATOR.createFromParcel(parcel);
}
}
ใน Kotlin คุณจะใช้ฟิลด์ CREATOR โดยตรงไม่ได้ แต่ให้ใช้
kotlinx.parcelize.parcelableCreator แทน
// import kotlinx.parcelize.parcelableCreator fun userFromParcel(parcel: Parcel): User { return parcelableCreator<User>().createFromParcel(parcel) }
ข้ามพร็อพเพอร์ตี้จากการซีเรียลไลซ์
หากต้องการข้ามพร็อพเพอร์ตี้บางรายการไม่ให้แยกวิเคราะห์ ให้ใช้
@IgnoredOnParcelคำอธิบายประกอบ นอกจากนี้ ยังใช้กับพร็อพเพอร์ตี้ภายในเนื้อหาของคลาสเพื่อปิดเสียงคำเตือนเกี่ยวกับพร็อพเพอร์ตี้ที่ไม่ได้ซีเรียลไลซ์ได้ด้วย
พร็อพเพอร์ตี้ของตัวสร้างที่มีคำอธิบายประกอบ @IgnoredOnParcel ต้องมีค่าเริ่มต้น
@Parcelize class MyClass( val include: String, // Don't serialize this property @IgnoredOnParcel val ignore: String = "default" ) : Parcelable { // Silence a warning @IgnoredOnParcel val computed: String = include + ignore }
ใช้ android.os.Parcel.writeValue เพื่อจัดลำดับพร็อพเพอร์ตี้
คุณสามารถใส่คำอธิบายประกอบประเภทด้วย @RawValue เพื่อให้ Parcelize ใช้
Parcel.writeValue สำหรับพร็อพเพอร์ตี้นั้น
@Parcelize class MyClass(val external: @RawValue ExternalClass) : Parcelable
การดำเนินการนี้อาจล้มเหลวในรันไทม์หากค่าของพร็อพเพอร์ตี้ไม่ได้รับการรองรับโดย Android โดยกำเนิด
Parcelize อาจกำหนดให้คุณใช้คำอธิบายประกอบนี้ด้วยเมื่อไม่มีวิธีอื่นในการ ทำให้พร็อพเพอร์ตี้เป็นอนุกรม
Parcelize ด้วยคลาสและอินเทอร์เฟซที่ปิดผนึก
Parcelize กำหนดให้คลาสที่จะแปลงเป็น Parcel ต้องไม่ใช่คลาส Abstract ข้อจำกัดนี้ไม่มีผลกับคลาสที่ปิดผนึก เมื่อใช้@Parcelizeคำอธิบายประกอบในคลาสที่ปิดผนึกแล้ว ก็ไม่จำเป็นต้องทำซ้ำสำหรับคลาสที่ได้มา
@Parcelize sealed class SealedClass : Parcelable { class A(val a: String) : SealedClass() class B(val b: Int) : SealedClass() } @Parcelize class MyClass(val a: SealedClass.A, val b: SealedClass.B, val c: SealedClass) : Parcelable
ตั้งค่า Parcelize สำหรับ Kotlin Multiplatform
ก่อน Kotlin 2.0 คุณสามารถใช้ Parcelize ได้โดยการแทนที่ชื่อแทนของคำอธิบายประกอบ Parcelize
ด้วย expect และ actual ดังนี้
// Common code
package example
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
expect annotation class MyParcelize()
expect interface MyParcelable
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
expect annotation class MyIgnoredOnParcel()
@MyParcelize
class MyClass(
val x: String,
@MyIgnoredOnParcel val y: String = ""
): MyParcelable
// Platform code
package example
actual typealias MyParcelize = kotlinx.parcelize.Parcelize
actual typealias MyParcelable = android.os.Parcelable
actual typealias MyIgnoredOnParcel = kotlinx.parcelize.IgnoredOnParcel
ใน Kotlin 2.0 ขึ้นไป ระบบจะไม่รองรับการแทนที่ชื่อคำอธิบายประกอบที่ทริกเกอร์ปลั๊กอิน
หากต้องการหลีกเลี่ยงปัญหานี้ ให้ระบุParcelizeคำอธิบายประกอบใหม่เป็นพารามิเตอร์
additionalAnnotationให้กับปลั๊กอินแทน
// Gradle build configuration
kotlin {
androidTarget {
compilerOptions {
// ...
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:additionalAnnotation=example.MyParcelize")
}
}
}
// Common code // package example @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) // No `expect` keyword here annotation class MyParcelize() expect interface MyParcelable @Target(AnnotationTarget.PROPERTY) @Retention(AnnotationRetention.SOURCE) expect annotation class MyIgnoredOnParcel() @MyParcelize class MyClass( val x: String, @MyIgnoredOnParcel val y: String = "" ) : MyParcelable
// Platform code // package example // No typealias for MyParcelize here actual typealias MyParcelable = android.os.Parcelable actual typealias MyIgnoredOnParcel = kotlinx.parcelize.IgnoredOnParcel
เนื่องจากอินเทอร์เฟซ Parcel ใช้ได้เฉพาะใน Android เท่านั้น Parcelize จึงไม่สร้างโค้ดใดๆ ในแพลตฟอร์มอื่นๆ ดังนั้นการติดตั้งใช้งาน actual ในแพลตฟอร์มเหล่านั้นจึงอาจว่างเปล่า นอกจากนี้ คุณยังใช้คำอธิบายประกอบที่ต้องอ้างอิงคลาส Parcel เช่น @WriteWith ในโค้ดทั่วไปไม่ได้
ฟีเจอร์ทดลอง
Data class serializer
พร้อมใช้งานตั้งแต่ Kotlin 2.1.0
คำอธิบายประกอบ DataClass ช่วยให้สามารถจัดรูปแบบคลาสข้อมูลเป็นอนุกรมได้ราวกับว่ามีการใส่คำอธิบายประกอบด้วย Parcelize คำอธิบายประกอบนี้ต้องใช้การเลือกใช้ kotlinx.parcelize.Experimental
// @file:OptIn(kotlinx.parcelize.Experimental::class) data class C(val a: Int, val b: String) @Parcelize class P(val c: @DataClass C) : Parcelable
เครื่องมือสร้างหลักและพร็อพเพอร์ตี้ทั้งหมดของเครื่องมือสร้างนั้นต้องเข้าถึงได้จากคลาส Parcelable นอกจากนี้ Parcelize ต้องรองรับพร็อพเพอร์ตี้ของตัวสร้างหลักทั้งหมดของคลาสข้อมูลด้วย
หากเลือก Custom Parcelers คุณควรระบุในคลาส
Parcelable ไม่ใช่คลาสข้อมูล
หากคลาสข้อมูลใช้ Serializable พร้อมกัน @DataClass
คำอธิบายประกอบจะมีลำดับความสำคัญสูงกว่า
android.os.Parcel.writeSerializable
จะไม่ถูกนำมาใช้
กรณีการใช้งานจริงสำหรับฟีเจอร์นี้คือการทำให้ kotlin.Pair เป็นอนุกรม
อีกตัวอย่างที่มีประโยชน์คือการลดความซับซ้อนของโค้ดแบบหลายแพลตฟอร์ม โค้ดทั่วไปสามารถประกาศชั้นข้อมูลเป็นคลาสข้อมูล ซึ่งโค้ด Android สามารถเพิ่มตรรกะการเรียงอันดับได้ ซึ่งจะช่วยลดความจำเป็นในการใช้คำอธิบายประกอบและนามแฝงประเภทเฉพาะของ Android ในโค้ดทั่วไป
// Common code: data class MyData(val x: String, val y: MoreData) data class MoreData(val a: String, val b: Int) // Platform code: @OptIn(kotlinx.parcelize.Experimental::class) @Parcelize class DataWrapper(val wrapped: @DataClass MyData) : Parcelable
พารามิเตอร์ที่ไม่ใช่ val หรือ var ในตัวสร้างหลัก
พร้อมใช้งานตั้งแต่ Kotlin 2.1.0
หากต้องการเปิดใช้ฟีเจอร์นี้ ให้เพิ่ม experimentalCodeGeneration=true ลงในอาร์กิวเมนต์ปลั๊กอิน parcelize
kotlin {
compilerOptions {
// ...
freeCompilerArgs.addAll("-P", "plugin:org.jetbrains.kotlin.parcelize:experimentalCodeGeneration=true")
}
}
ฟีเจอร์นี้ยกเลิกข้อจำกัดที่อาร์กิวเมนต์ของตัวสร้างหลักต้องเป็น val หรือ var ซึ่งจะช่วยแก้ปัญหาหนึ่งของการใช้ Parcelize กับการรับค่า
ซึ่งก่อนหน้านี้ต้องใช้พร็อพเพอร์ตี้ open
// base parcelize @Parcelize open class Base(open val s: String) : Parcelable @Parcelize class Derived( val x: Int, // all arguments have to be `val` or `var` so we need to override // to not introduce new property name override val s: String ) : Base(s)
// experimental code generation enabled
@Parcelize
open class Base(val s: String): Parcelable
@Parcelize
class Derived(val x: Int, s: String): Base(s)
ระบบอนุญาตให้ใช้พารามิเตอร์ดังกล่าวในอาร์กิวเมนต์ของตัวสร้างคลาสฐานเท่านั้น ไม่อนุญาตให้อ้างอิงถึงเนื้อหาดังกล่าวในเนื้อหาของชั้นเรียน
@Parcelize
class Derived(s: String): Base(s) { // allowed
@IgnoredOnParcel
val x: String = s // ERROR: not allowed.
init {
println(s) // ERROR: not allowed
}
}
ความคิดเห็น
หากพบปัญหาเกี่ยวกับปลั๊กอิน kotlin-parcelize Gradle คุณสามารถ
รายงานข้อบกพร่อง