[Android / Kotlin] SavedStateHandle

Subeen·2024년 1월 12일
1

Android

목록 보기
40/73

아래 표를 보면 ViewModel을 사용하는 것만으로도 데이터의 손질을 방지하고 유지할 수 있다. 하지만 ViewModel의 경우 프로세스 중단 시에 유지가 되지 않으므로 시스템에서 시작된 프로세스를 중단 한 경우에도 UI상태를 유지하고자 한다면 저장된 인스턴스 상태 (Saved Instance State)를 사용해야 한다.

  • UI 상태를 유지하기 위한 옵션
ViewModel저장된 인스턴스 상태영구 저장소
저장소 위치메모리 내디스크에 직렬화디스크 또는 네트워크 내
구성 변경 시에도 유지
시스템에서 시작된 프로세스 중단 시에도 유지아니요
사용자의 완전한 활동 닫기 /onFinish() 시에도 유지아니요아니요
데이터 제한복잡한 객체도 괜찮지만 사용 가능한 메모리에 의해 공간이 제한 됨원시 유형 및 문자열과 같은 단순하고 작은 객체만 해당디스크 공간 또는 네트워크 리소스에서 검색하는 비용/시간에 의해서만 제한됨
읽기/쓰기 시간빠름(메모리 액세스만)느림(직렬화/역직렬화 및 디스크 액세스 필요)느림(디스크 액세스 또는 네트워크 트랜잭션 필요)
  • 저장된 인스턴스 상태는 ViewModel의 일부로 onSaveInstanceState()rememberSaveable API와 SavedStateHandle을 포함한다.
  • 저장된 인스턴스를 어떠한 API로 사용할지는 상태가 유지된 위치와 필요한 로직에 따라 다르다.
    • SavedStateHandle : 비즈니스 로직에 사용되는 상태의 경우, ViewModel에 유지하고 SavedStateHandle을 사용하여 저장한다.
    • onSaveInstanceState, rememberSaveable : UI 로직에 사용되는 상태의 경우

SavedStateHandle

  • SavedStateHandle을 ViewModel에서 다루기 위해서는 SavedStateHandle을 받는 생성자를 ViewModel에 포함해야 한다.
class SignUpViewModel(private val handle: SavedStateHandle) : ViewModel() {
	...
}
  • SavedStateHandle을 받도록 ViewModel 인스턴스를 생성하려면 AbstractSavedStateViewModelFactory를 확장하는 Factory 클래스를 만들어야 한다.
  • 만약, ViewModel 생성자 파라미터가 Application, SavedStateHandle 또는 SavedStateHandle이라면 이미 정의된 SavedStateViewModelFactory 클래스를 이용하여 AbstractSavedStateViewModelFactory를 대신할 수 있다.
class SignUpActivity : AppCompatActivity() {

    companion object {
        const val EXTRA_ENTRY_TYPE = "extra_entry_type"
        const val EXTRA_USER_TYPE = "extra_user_entity"

        fun newIntent(
            context: Context,
            entryType: SignUpEntryType,
            entity: SignUpUserEntity? = null
        ): Intent =
            Intent(
                context,
                SignUpActivity::class.java
            ).apply {
                putExtra(EXTRA_ENTRY_TYPE, entryType.ordinal)
                putExtra(EXTRA_USER_TYPE, entity)
            }
    }

	// SavedStateViewModelFactory를 이용한 ViewModel 인스턴스화 
    private val signUpViewModel: SignUpViewModel by viewModels {
        SavedStateViewModelFactory(application, this, intent?.extras)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }
}
  • SavedStateHandle 객체는 저장된 상태에서 객체를 작성하고 저장된 상태에서 객체를 검색할 수 있게 하는 키-값 맵이다. 이러한 값은 시스템에서 프로세스가 중단된 후에도 유지되며 동일한 객체를 계속 사용할 수 있다.

  • 키-값 맵에 필요한 메서드

    • get(String key)
    • contains(String key)
    • remove(String key)
    • set(String key, T Value)
    • keys()
  • Intent로 넘어온 데이터를 출력해보면 다음과 같이 key와 key에 해당하는 값을 확인할 수 있다.

class SignUpViewModel(private val handle: SavedStateHandle) : ViewModel() {

    private val _entryType: MutableLiveData<SignUpEntryType> = MutableLiveData()
    val entryType: LiveData<SignUpEntryType> get() = _entryType

    private val _userEntity: MutableLiveData<SignUpUserEntity> = MutableLiveData()
    val userEntity: LiveData<SignUpUserEntity> get() = _userEntity

    init {
        // Intent로 전달받은 데이터 확인
        handle.keys().forEach { key ->
            Log.d("SignUpViewModel", "Received [$key]=[${handle.get<Any>(key)}]")
        }
    }
    
}
Received [extra_user_entity]=[SignUpUserEntity(name=스파르타, email=hello, emailService=r.com)]
Received [extra_entry_type]=[1]
  • ViewModel의 SavedStateHandle을 이용하여 데이터를 저장하고 받아오기
class SignUpViewModel(private val handle: SavedStateHandle) : ViewModel() {

    private val _entryType: MutableLiveData<SignUpEntryType> = MutableLiveData()
    val entryType: LiveData<SignUpEntryType> get() = _entryType

    private val _userEntity: MutableLiveData<SignUpUserEntity> = MutableLiveData()
    val userEntity: LiveData<SignUpUserEntity> get() = _userEntity

	...
    
    init {
        // SavedStateHandle 객체에서 SignUpEntryType형의 EXTRA_ENTRY_TYPE 키를 가진 객체 받아오기
        val entryTypeOrdinal = handle[EXTRA_ENTRY_TYPE] ?: SignUpEntryType.CREATE.ordinal
        setEntryType(SignUpEntryType.values()[entryTypeOrdinal])

		// SavedStateHandle 객체에서 SignUpEntryType형의 EXTRA_USER_TYPE 키를 가진 객체 받아오기
        if (entryType.value == SignUpEntryType.UPDATE) { 
            setUserEntity(handle[EXTRA_USER_TYPE] ?: SignUpUserEntity("", "", ""))
        }
    }

    private fun setEntryType(entryType: SignUpEntryType) {
        // savedStateHandle에 저장하기
        handle[EXTRA_ENTRY_TYPE] = entryType.ordinal
        _entryType.value = entryType
    }

    private fun setUserEntity(entity: SignUpUserEntity) {
		// savedStateHandle에 저장하기
        handle[EXTRA_USER_TYPE] = entity
        _userEntity.value = entity
    }
    
}

참고
SavedStateHandle과 함께 ViewModel의 상태를 저장하자
[Jetpack] ViewModel의 SavedStateHandle

profile
개발 공부 기록 🌱

0개의 댓글