익숙한 Serializable과 Parcelable
Serializable과 Parcelable, 익숙한 녀석들이다. Bundle에 객체를 담아 Intent와 arguments를 통해 다른 곳으로 전달하려면 객체의 클래스가 둘 중 하나를 구현(상속)해야 한다.
나는 지금까지 Serializable이 비효율적이라고 해서 코틀린에서 제공하는 @Parcelize 을 사용하거나, 별 생각 없이 Serializable을 사용하곤 했다.
그러다 둘이 어떤 차이가 있고, 왜 Serializable이 비효율적인지 알아보기로 했다.
직렬화
Serializable: 직렬화 가능한, ‘직렬화’ 많이 들어보긴 했는데..
- 직렬화(serialization): 객체를 바이트 단위의 연속적인 데이터(바이트 스트림)로 변경하는 작업
- 역직렬화(deserialization): 바이트 스트림을 원래 객체로 변환하는 작업
Serializable과 Parcelable은 모두 직렬화와 관련이 있다. 객체를 주고 받으려면 객체를 직렬화 해야 한다.
왜 직렬화가 필요할까?
직렬화는 서로 다른 메모리 영역을 갖는 컴포넌트 간 객체를 주고 받을 때 사용한다. 객체는 대부분 다른 객체를 가리키는 참조 필드를 갖고 있는데, 이 주소 값을 다른 메모리에서는 사용할 수가 없다. 따라서, 이 참조 변수를 그것이 가리키는 실제 값으로 변환하는 작업이 필요하다.
Serializable
Serializable은 Java에서 제공하는 표준 인터페이스이다. Serializable을 구현한 클래스는 직렬화 대상이 된다.
장점
- 사용하기 편하다. 따로 구현할 코드가 없다.
단점
- Reflection을 사용하기 때문에 느리고 메모리를 많이 쓴다.
Reflection
- Reflection: Java에서 제공하는 API로, 런타임에 객체의 정보를 분석하는 기법
객체의 프로퍼티는 런타임에 동적으로 변하기 때문에 컴파일 타임에 결정할 수 없다. 그래서 Reflection을 통해 정보를 분석한 후 직렬화 한다.
하지만 Reflection 과정에서 여러 중간 객체가 생성된다. 그리고 중간 객체를 생성하고 GC를 통해 제거하는 과정에서 메모리와 CPU를 사용하므로 그만큼 리소스가 소모되는 작업이라고 할 수 있다.
Parcelable
안드로이드 SDK에서 제공하는 인터페이스이다. 이것을 구현한 클래스의 객체는 직렬화 가능하다.
Parcelable은 Reflection이 런타임에 하는 작업을 개발자가 대신 한다. 개발자가 직접 직렬화/역직렬화 하는 로직을 작성해야 한다.
장점
Serializable에 비해 빠르고 리소스를 덜 소모한다.
Reflection 과정 없이 미리 작성된 로직을 바탕을 빠르게 직렬화/역직렬화 할 수 있다.
단점
직접 작성해야 하는 코드가 많다. class User(val id: Int, val name: String)에 대해 필요한 코드는 다음과 같다.
class User(val id: Int, val name: String) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString() ?: ""
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
parcel.writeString(name)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<User> {
override fun createFromParcel(parcel: Parcel): User {
return User(parcel)
}
override fun newArray(size: Int): Array<User?> {
return arrayOfNulls(size)
}
}
}
(좀 길긴 하다;;)
Serializable vs Parcelable
그래서 어떤 걸 사용하는 게 좋을까? 개발 서적을 볼 때마다 항상 보는 문장이 있는데, 프로그래밍에 정답은 없다는 것이다.
개인적인 견해로는, 요새 하드웨어 성능이 좋으니까 속도가 중요한 상황이라면 Parcelable을, 그렇지 않다면 Serializable을 사용해도 괜찮지 않을까? 저렇게 긴 코드를 작성하는 것보다 핵심 로직에 집중하는 게 더 나을 것 같다.
하지만 Parcelize가 있다!
사실 현재 코틀린에서는 Parcelize를 제공하고 있다. Parcelize는 Serializable의 간편함과 Parcelable의 속도 측면의 장점을 모두 누릴 수 있는 기술로, Parcelable의 구현을 자동으로 생성해준다.
plugins {
id("kotlin-parcelize")
}
import kotlinx.parcelize.Parcelize
@Parcelize
class User(val name: String, val email: String): Parcelable
위와 같이 Parcelable 인터페이스를 구현하고 @Parcelize 어노테이션을 달면 구현 끝!
참조
'TIL > 안드로이드' 카테고리의 다른 글
[Android] Context란? (feat. 메모리 누수) (0) | 2023.04.12 |
---|---|
[Android] 비트맵 크기 최적화 로딩하기 (0) | 2023.03.18 |
[Android] Thread, Looper, Handler 기본 개념 (0) | 2023.03.16 |
[TIL] 안드로이드 - Java와 Kotlin이 호환되는 이유 (0) | 2022.03.12 |