포스팅 환경
- M1 Mac OS Ventura 13.4.1
- Android Studio Giraffe | 2022.3.1 Patch 1 (ARM)
- Gradle 7.5
- Android Build Gradle 7.4.2
- Kolin 1.7.20
- Target SDK 33
화면 회전?
Android 에서는 사용자의 기기가 회전 함에 따라
실행 중인 앱의 화면 회전과 같은 구성 변경(Configuration Changes)이 발생 할 때
앱의 Activity 를 완전 파괴하고, 새로운 구성에 맞게 Activity 를 다시 생성한다.
주의할 점은 화면의 상태가 다 날아가기 때문에 Activity 가 파괴되기 전 데이터를 저장하는 것이 중요하다.
화면 회전 시, 생명주기에 따라 호출되는 함수
Activity
- onPause
- onStop
- onSaveInstanceState(outState: Bundle)
- onDestroy
- onCreate
- onStart
- onRestoreInstanceState(savedInstanceState: Bundle)
- onResume
Fragment
- onPause
- onStop
- onSaveInstanceState(outState: Bundle)
- onDestroyView
- onDestory
- onDetach
- onCreate
- onCreateView
- onViewCreated
- onViewStateRestored(savedInstanceState: Bundle)
- onResume
Activity + Fragment
- Fragment::onPause
- Activity:onPause
- Fragment::onStop
- Activity::onStop
- Fragment::onSaveInstanceState(outState: Bundle)
- Activity::onSaveInstanceState(outState: Bundle)
- Fragment::onDestroyView
- Fragment::onDestory
- Fragment::onDetach
- Activity::onDestory
- Fragment::onAttach
- Fragment::onCreate
- Activity::onCreate
- Fragment::onCreateView
- Fragment::onViewCreated
- Fragment::onViewStateRestored(savedInstanceState: Bundle)
- Activity::onStart
- Activity:onRestoreInstanceState(savedInstanceState: Bundle)
- Activity::onResume
- Fragment::onResume
화면 회전 시, 데이터 저장 전략
화면이 회전하게 되면 Activity 와 View 가 완전히 파괴 되었다가 재생성 되기 때문에
화면의 상태를 유지하기 위해서는 View 의 State 를 저장할 필요가 있다.
Activity
ViewModel 에 저장하는 방법
응? 여기서 궁금하신 분들이 있을 수도 있다.
분명 Activity 는 onDestory 가 호출 되고
그렇다면 ViewModel 의 onCleared 가 호출 되서
ViewModel 의 인스턴스도 날아가기 때문에
ViewModel 에 데이터를 저장하는게 맞는건가? 라고 생각할 수 있다.
저도 그렇게 생각했었습니다.
이건 Activity 와 ViewModel 의 생명주기에 대해서 정확히
알고(?) 있어서 그런 것이니 잘못된 것이 아닙니다.
그 이유는 Activity 가 상속받은 ComponentActivity 를 살펴볼 필요가 있습니다.
먼저 아래의 코드를 보면 viewModel::onCleared() 함수를 호출하는 조건에
isChangingConfigurations() 함수가 조건으로 들어가 있는 것을 볼 수 있습니다.
ComponentActivity.java
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
isChangingConfigurations 함수의 주석을 보면
true if the activity is being destoryed in order to recreate it with a new configuration
즉, 새로운 화면 구성을 위해서 Activity 를 파괴했다가 재생성 되는 경우 에는 이 함수의 반환 값은 true 라는 소리입니다.
Activity.java
/**
* Check to see whether this activity is in the process of being destroyed in order to be
* recreated with a new configuration. This is often used in
* {@link #onStop} to determine whether the state needs to be cleaned up or will be passed
* on to the next instance of the activity via {@link #onRetainNonConfigurationInstance()}.
*
* @return If the activity is being torn down in order to be recreated with a new configuration,
* returns true; else returns false.
*/
public boolean isChangingConfigurations() {
return mChangingConfigurations;
}
에.. 그러니까 정상적인 종료가 아니라
화면 회전 등과 같은 구성 변경에 의한 이유에는 ViewModel::onCleared 함수가 호출 되지 않습니다.
그러니까 마음 놓고(?) ViewModel 에 데이터를 저장해두어도 된다는 소리 입니다. (휴우 ^^)
Bundle 을 이용한 State 저장
ViewModel 을 제외하고 또 다른 방법으로는 Bundle 에 View 에 대한 상태데이터를 저장하는 방법이 있습니다.
이는 Activity 가 파괴 되기 전에 onSaveInstanceState(outState: Bundle) 함수가 호출되고
Activity 재생성 후 화면에 보이기 전에 호출되는 onRestoreInstanceState(savedInstanceState: Bundle)
함수를 통해 저장하고 가져올 수 있습니다.
onSaveInstanceState
Activity::onStop 이 호출 되고 onDestory 함수가 호출 되기 직전에 호출 됩니다.
이 때 필요한 화면의 상태 값들을 저장하면 됩니다.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("SOME_STATE_KEY", "SOME_VALUE")
}
onRestoreInstanceState
Activity::onStart 가 호출 되고, onResume 이 호출 되기 직전에 호출 됩니다.
이 때 필요한 화면의 상태 값들을 가져오면 됩니다.
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
savedInstanceState.getString("SOME_STATE_KEY", "SOME_VALUE")
}
주의할점
Bundle 의 경우 Parcelable 의 구현체이기 때문에 용량의 제한이 있습니다.
Bundle 에 넣은 데이터의 경우 총합이 최대 1MB 를 넘을 경우
TransactionTooLargeException 이 발생합니다.
이 때는 Bundle 에 모든 데이터를 넣기 보다.
데이터를 파일이나 Local Database 에 저장하고
파일의 path 나 데이터의 key 나 id 에 해당하는 값만 Bundle 에 저장하여야 합니다.
마무리
이상으로 화면 회전에 따른 대응 방법을 알아보았습니다.
감사합니다. :)
'Develop > Android' 카테고리의 다른 글
[Android] SDK 33 이상에서 PackageInfo 가져오기 (0) | 2024.02.26 |
---|---|
[Android] Gradle 7.2 이상 플러그인, 의존성, 저장소 추가 방법 (0) | 2024.02.19 |
[Android] - Gradle Dependency 최신 버전 쉽게 확인하기 (0) | 2022.10.21 |
[Android] - 코드로 기기 화면 켜지게 하기 (0) | 2022.10.18 |
[Android] - Assets 내부의 파일 접근하기 (0) | 2022.09.23 |