Bundle
String key에 다양한 Parcelable 값으로 매핑
key, value의 Map 형태의 데이터 구조
Parcelable와 Bundle는 IPC/바인더, 인텐트가 있는 활동 간 트랜젝션, 구성 변경 간에 일시적인 상태를 저장하는 것과 같이
프로세스 경계를 넘어 사용하도록 의도된 객체
Parcel는 일반적인 직렬화 메커니즘이 아니며, Parcel를 디스크에 데이터를 저장하거나 네트워크를 통해 전송해서는 안 됩니다.
Bundle은 parcel을 사용하여 마샬링(marshalling) 및 언마샬링(unmarshalling)하는 데 매우 최적화되어 있습니다.
데이터를 저장하거나 전송할 목적으로 데이터 구조 또는 개체를 한 표현에서 다른 표현으로 변환하는 프로세스
마샬링(marshalling) : 객체를 byte stream으로 변환하는 과정이나 XML문서 같은 파일로 변환하는 등의 데이터를 잘 정돈/정렬된 상태로 만드는 것,
여기서는 바인더 트랜잭션(BC_TRANSACTION)에 포함할 목적으로 상위 레벨 애플리케이션 데이터 구조를 패킷으로 변환하는 절차이다.
언마샬링(unmarshalling) : 마샬링(marshalling)된 파일을 복원하는 것,
여기서는 바인더 트랜잭션을 통해 수신된 데이터(BR_TRANSACTION)에서 상위 수준의 애플리케이션 데이터 구조를 재구성하는 절차이다.
public final class Bundle extends BaseBundle implements Cloneable, Parcelable
활동(Activity) 간 데이터 전송
Sending data between activities
새로운 Activity를 시작하기 위해 사용되는 Intent 객체에 putExtra 메서드를 사용하여 매개변수 전달할 수 있다.
val intent = Intent(this, MyActivity::class.java).apply {
putExtra("media_id", "a1b2c3")
// ...
}
startActivity(intent)
OS는 이때,
- intent의 Bundle를 포장(parcels)한다
- 새(new) Activity를 생성
- 데이터를 다시 풀어(un-parcels) intent를 Activity에 전달
이때 bundle의 value로 원시 타입 데이터(Int, Long, Double, String 등)가 아닌 경우
custom(사용자 정의 class) parcelables 사용할 수 있습니다.
어떤 경우에는 복합 또는 복잡한 객체를 활동 간에 전송하는 메커니즘이 필요할 수 있습니다.
예를 들어 목록 UI에서 목록을 클릭해서 상세 페이지로 넘어갈 경우,
해당 목록의 데이터를 상세 페이지에 그대로 넘겨주고 싶을 경우가 있다.
목록에 대한 id만을 넘기고 상세 페이지에서 id에 대해 다시 데이터 조회를 할 수 있지만,
UI 렌더링 속도 등을 고려하여 일부 데이터를 그대로 넘겨주는 방법을 선택할 수 있음.
필요 데이터
- 제목
- 글 내용
- 북마크 여부
bundle에 제목, 글 내용, 북마크에 대한 key를 따로따로 생성해서 각각 넘겨줄 수도 있지만 이것은 매우 번거롭다.
이러한 UI에서 필요 데이터를 data class로 하나의 데이터로 관리하게 되는데
이 data class를 자체를 bundle에 담아 넘겨줄 수 있다.
다음은 data class를 bundle에 담기 위해 직렬화하는 방법이다.
이 부분을 custom(사용자 정의 클래스) parcelables 사용할 수 있다고 보는 것이다.
사용자 정의 클래스 parcelables 예시 (직렬화)
data class Post(
val title: String,
val contents: String,
val isBookMark: Boolean
) : Serializable
@Parcelize
data class Post(
val title: String,
val contents: String,
val isBookMark: Boolean
) : Parcelable
이때 넘겨주는 데이터의 크기가 무한정하지 않는데,
bundle 크기 제한은 다음과 같다.
Intent 데이터 크기 제한
- N KB 제한
bundle 크기 제한을 초과할 경우 시스템에서 TransactionTooLargeException 예외를 발생시킨다.
Intent?
구성요소에게 작업을 요청하는 데 사용할 수 있는 메시지 객체
Intent는 고수준으로 추상화시킨 IPC
startActivity에서 intent가 필요한 이유(직렬화(마실링)가 필요한 이유)는 무엇일까
Activity는 같은 앱 내의 Activity를 호출할 수 있지만, 다른 앱의 구성요소 등의 자원을 호출(암시적 인텐트) 할 수 있다.
즉, 서로 다른 프로세스간의 통신일 수 있는 것이다.
하지만 같은 앱 내의 Activity을 실행하는 등
Activity 간 데이터 교환에 직렬화(마실링)가 필요한 이유는 무엇일까
Activity는 각자의 독립적인 lifecycler을 가지고 있으며 프로세스를 통해 언제든 파괴될 수 있다.
그렇기 때문에 Activity 메모리에 가지고 있는 데이터를 공유한다면, 특정 Activity가 파괴되었을 때 해당 값을 가져오지 못할 가능성이 생긴다.
일단, Manifest에서 Activity 선언 시에 process를 별도로 지정하지 않으면 하나의 Application에 선언된 Activity는 동일한 Process Id를 갖는다. 하지만 위의 문제처럼 특정 Activity가 파괴될 가능성이 있고 거기서 발생할 수 있는 문제점을 해결하기 위해 기본적으로 android에서는 Activity 간의 데이터 전달은 Intent를 사용한다.
프로세스 간 데이터 전송
Sending data between processes
android는 프로세스 간의 데이터를 전달할 때 바인더(IBinder)를 통해 parcel 객체를 전달한다.
이 작업에서는 custom(사용자 정의 클래스) parcelables를 사용하지 말아야 한다.
이유는 다음과 같다
bundle을 전송할 경우 전송 앱과 수신 앱 모두에서 언마샬링 할 수 있는 정보를 가지고 있어야 한다.
하지만 사용자 정의 클래스일 경우 수신 앱에서 사용자 정의 클래스 정의해 놓아도 수신 앱에서는 이를 확인할 수 없기 때문에 사용자 정의
사용자 정의 클래스를 앱에 정의해 놓았어도 parcelable을 시스템에 전송하려고 하면 오류가 발생할 수 있다.
시스템은 앱에서 다른 앱으로 사용자 정의 객체를 전송하는 경우 전송 앱과 수신 앱 모두에 정확히 동일한 버전의 사용자 정의 클래스가 있는지 확인해야 합니다
시스템은 자신이 알지 못하는 클래스를 언마샬링 할 수 없습니다.
예를 들어, 앱은 AlarmManager클래스를 사용하여 알람을 설정하고 알람 인텐트에 사용자 정의 Parcelable를 사용할 수 있다.
그런데 이때 알람이 울리면 시스템은 Bundle 반복 횟수를 추가하기 위해 인텐트에 extra bundle을 수정한다.
이 수정으로 인해 시스템이 extra bundle에서 사용자 정의 Parcelable를 제거할 수 있습니다.
이 제거로 인해 앱이 수정된 알람 인텐트를 수신하면 앱이 더 이상 존재하지 않는 extra 데이터를 수신할 것으로 예상하기 때문에 앱이 충돌(비정상 종료)할 수 있다.
바인더 트랜젝션 버퍼 크기 제한
- 1MB인 고정 크기
주의할 점은 이때 크기 제한은 프로세스에 대해 진행 중인 모든 트랜젝션에서 공유되며,
이 제한은 Activity 수준이 아니라 프로세스 수준에 있으므로
이러한 트랜잭션에는 onSaveInstanceState, startActivity 및 시스템과의 모든 상호 작용과 같은 앱의 모든 바인더 트랜잭션이 포함한다.
크기 제한을 초과할 경우, TransactionTooLargeException이 throw 됩니다.
특히, savedInstanceState 경우 데이터 양을 작게 유지해야하는데,
이는 Activity의 구성 변경이 일어나는 등의 Activity가 갑작스럽게 종료되었어도 해당 Activity로 돌아왔을 때에도 해당 데이터를 보존하기 위해 사용하는 기법이다.
이는 Activity 프로세스가 종료된 경우에도 사용자가 해당 Activity(활동)으로 돌아갈 수 있는 한 시스템 프로세스가 제공된 데이터를 보관해야 한다는 뜻으로 데이터 양을 작게 유지해야 한다.
저장된 데이터는 50k 미만을 권장한다.
참고: Android 7.0(API 레벨 24) 이상에서 시스템은 TransactionTooLargeException을 런타임 예외로 throw 합니다. Android의 하위 버전에서 시스템은 logcat에 경고만 표시합니다
출처 및 참고자료
https://developer.android.com/guide/components/activities/parcelables-and-bundles
Parcelable 및 번들 | Android Developers
Parcelable 및 번들은 IPC/바인더 트랜잭션과 같은 프로세스 경계 전반에서 인텐트가 있는 활동 간에 사용하고 구성 변경 시 일시적인 상태를 저장하기 위한 것입니다.
developer.android.com
https://developer.android.com/reference/android/os/Bundle#summary
Bundle | Android Developers
developer.android.com
의도(Intent)를 알고 사용하기
Android IPC mechanism and how we can use it
medium.com
https://kimansu.medium.com/9-android-study-activity-%EC%99%80-lifecycle-%E2%85%B2-930015a2479f
#9 Android Study — Activity 와 Lifecycle Ⅲ
오늘은 이 주제를 마무리 해보도록 해보자…
kimansu.medium.com
'Android > 학습' 카테고리의 다른 글
[Android] ViewModel Instance 생성 with ViewModelProvider (3) | 2024.11.28 |
---|---|
[Android] Jetpack ViewModel (1) | 2024.11.14 |
[CS] SOLID - 객체지향 설계의 원칙 (7) | 2024.09.25 |
[CS] 객체지향 프로그래밍(OOP: Object-Oriented Programming)과 절차지향 프로그래밍(Procedural Programming) (1) | 2024.09.04 |
[Android] Android 권장 앱 아키텍처를 통해 보는 ViewModel과 Repository (0) | 2022.09.18 |