안드로이드는 화면을 회전시키는 것과 같이 configuration change 가 발생하면 액티비티가 재생성된다. (onDestroy() 후에 onCreate() 부터 라이프사이클이 다시 시작되기 때문) 따라서 뷰에 어떤 정보를 저장하고있었다면 모두 잃게 될 것이다. 이를 해결하기 위해 다양한 방법이 공식문서에 소개되어 있지만, 여기에 자세히 설명되어있지 않은 방법이 있다. 내가 영어를 못해서그렇지, 이미 있을 수도 있음
그 방법은 바로 View 클래스의 static inner class 로 BaseSavedState에 내가 저장하고 싶은 정보를 담아 복원하면 된다. Configuration change 가 일어나도 정보가 저장될 뿐더러 메모리 부족으로 프로세스가 kill 당하더라도 데이터를 유지할 수 있다. 원리는 Parcelable 로 감싸서 IPC 로 어딘가에 저장하도록 구현되어있겠지..? (내 추측)
TextView 나 EditText 도 위 방법으로 데이터를 저장/복원하고있기 때문에, 세팅된 데이터는 configuration change 가 일어나도 데이터가 사라지지 않는걸 확인할 수 있다. 아래 TextView 클래스 내에 구현된 SavedState 를 보면 대충 어떤 느낌인지 이해할 수 있을 것이다.
더보기
더보기
public static class SavedState extends BaseSavedState {
int selStart = -1;
int selEnd = -1;
CharSequence text;
boolean frozenWithFocus;
CharSequence error;
ParcelableParcel editorState; // Optional state from Editor.
SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(selStart);
out.writeInt(selEnd);
out.writeInt(frozenWithFocus ? 1 : 0);
TextUtils.writeToParcel(text, out, flags);
if (error == null) {
out.writeInt(0);
} else {
out.writeInt(1);
TextUtils.writeToParcel(error, out, flags);
}
if (editorState == null) {
out.writeInt(0);
} else {
out.writeInt(1);
editorState.writeToParcel(out, flags);
}
}
@Override
public String toString() {
String str = "TextView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " start=" + selStart + " end=" + selEnd;
if (text != null) {
str += " text=" + text;
}
return str + "}";
}
@SuppressWarnings("hiding")
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private SavedState(Parcel in) {
super(in);
selStart = in.readInt();
selEnd = in.readInt();
frozenWithFocus = (in.readInt() != 0);
text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readInt() != 0) {
error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
editorState = ParcelableParcel.CREATOR.createFromParcel(in);
}
}
}
그리고 마지막으로 아래 예제코드와 함께 설명은 주석으로 정리를 끝내겠다. TextView 에서는 좀 더 상세하게 구현한 것 같은데, 아래만 해도 충분하다. (내 경험)
// View - 상태 복원 메서드
override fun onRestoreInstanceState(state: Parcelable?) {
if (state is SavedState) {
// 부모의 상태 복구
super.onRestoreInstanceState(state.superState)
// 내 상태(커스텀뷰) 복구
커스텀뷰.정보1 = state.정보1
커스텀뷰.정보2 = state.정보2
커스텀뷰.정보3 = state.정보3
} else {
super.onRestoreInstanceState(state)
}
}
// View - 상태 저장 메서드
override fun onSaveInstanceState(): Parcelable? {
val parcelable = super.onSaveInstanceState()
// 저장할게 없으면 그대로 null 리턴
if (parcelable == null) {
return parcelable
}
// 저장할게 있으면 부모 상태와 함께 내 상태 저장
return SavedState(parcelable).apply {
정보1 = 커스텀뷰.정보1
정보2 = 커스텀뷰.정보2
정보3 = 커스텀뷰.정보3
}
}
// View 안에 inner class 로 작성
private class SavedState : BaseSavedState {
constructor(superState: Parcelable) : super(superState)
// Parcel 객체에 상태가 저장되어있으므로, 이 객체로부터 저장된 상태를 복구하기 위한 생성자
// 순서가 매우 중요
private constructor(source: Parcel) : super(source) {
커스텀뷰.정보1 = source.readParcelable(Parcelable클래스::class.java.classLoader)
커스텀뷰.정보2 = source.readLong()
커스텀뷰.정보3 = source.readInt()
}
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<SavedState> {
override fun createFromParcel(source: Parcel): SavedState {
return SavedState(source)
}
override fun newArray(size: Int): Array<SavedState?> {
return arrayOfNulls(size)
}
}
}
var 정보1: Parcelable클래스? = null
var 정보2: Long = 0L
var 정보3: Int = 0
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
// 순서가 매우 중요하다. 생성자와 동일해야함
out.writeParcelable(정보1, flags) // 여기 들어가는 flag 는 뭔지 안찾아봤다. 알아볼것...
out.writeLong(정보2)
out.writeInt(정보3)
}
}
그런데 사실 AAC 뷰모델을 사용하고있다면, 아마도 뷰에 데이터를 저장해야할 경우는 많이 없을 것이다..!
'Android > 지식저장소' 카테고리의 다른 글
[Android] 간단한 그리기 및 지우기 with S-Pen (1) | 2021.05.14 |
---|---|
[Android] ConstraintLayout Helper - Group (0) | 2021.01.11 |
[Android] 점선 그리기(Dotted line) (0) | 2021.01.04 |
[Android] RxJava2 + Retrofit2 에서 언제 call 이 cancel 되는가 고찰 (0) | 2020.09.10 |
[Android] RecyclerView 에서 엣지 스크롤 이펙트 숨기기 (0) | 2020.09.09 |