kotlin-parcelize 플러그인은 Parcelable 구현 생성기를 제공합니다.
Parcelable 지원을 포함하려면 앱의 build.gradle 파일에 Gradle 플러그인을 추가합니다.
Groovy
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를 사용하려면 모든 직렬화된 속성을 기본 생성자에서 선언해야 합니다. 플러그인은 클래스 본문에서 선언된 backing 필드가 있는 각 속성에 경고를 표시합니다. 또한 기본 생성자 매개변수의 일부가 속성이 아니면 @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 } } }
#supportedtypes
@Parcelize는 다음과 같이 다양한 유형을 지원합니다.
- 기본 유형 및 기본 유형의 박스 버전
- 객체 및 enum
CharSequenceStringDurationExceptionSize,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
- 여러 구체적인 구현:
- 지원되는 모든 유형의 배열
- 지원되는 모든 유형의 null 허용 버전
맞춤 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 주석을 사용하여 외부 parceler를 적용할 수 있습니다.
// 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) }
직렬화에서 속성 건너뛰기
일부 속성이 parcelize되지 않도록 하려면 @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를 사용하려면 parcelize할 클래스가 추상 클래스가 아니어야 합니다. 이 제한은 봉인된 클래스에는 적용되지 않습니다. @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
Kotlin 멀티플랫폼용 Parcelize 설정
Kotlin 2.0 이전에는 expect 및 actual로 Parcelize 주석에 별칭을 지정하여 Parcelize를 사용할 수 있었습니다.
// 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 이상에서는 플러그인을 트리거하는 주석의 별칭이 지원되지 않습니다. 이 문제를 해결하려면 플러그인에 additionalAnnotation 매개변수로 새 Parcelize 주석을 제공하세요.
// 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)을 공통 코드에서 사용할 수 없습니다.
실험용 기능
데이터 클래스 직렬 변환기
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에서 지원해야 합니다.
맞춤 Parceler는 선택한 경우 데이터 클래스가 아닌
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부터 사용할 수 있습니다.
이 기능을 사용 설정하려면 parcelize 플러그인 인수에 experimentalCodeGeneration=true를 추가하세요.
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 플러그인에서 문제가 발생하면 버그를 신고해 주세요.