Android 앱의 수명 주기를 관리할 때 백그라운드 리소스 회수 중에 사용자 상태를 유지하는 것은 원활한 사용자 환경의 핵심 구성요소입니다. 웹 워크플로를 통합하는 앱의 경우 WebView.saveState(Bundle)
를 사용하면 WebView의 탐색 기록과 상태를 Bundle로 직렬화할 수 있습니다.
이 데이터는 이후에 WebView.restoreState(Bundle)을 사용하여 복원할 수 있습니다.
그러나 표준 구현은 과도한 탐색 세션에서 트랜잭션 크기 제한에 도달할 수 있습니다. 이 페이지에서는 이러한 아키텍처 제한사항을 설명하고 탐색 기록을 유지하면서 메모리 관련 예외를 방지하는 전략을 제공합니다.
1MB 거래 한도 및 상태 삭제
Android는 savedInstanceState 내에 저장할 수 있는 총 데이터 볼륨에 엄격한 1MB 한도를 적용합니다. 이 1MB 예산은 전체 앱 프로세스에서 공유됩니다. 앱에 여러 WebView 인스턴스가 통합되어 있는 경우 집합적 탐색 상태와 기록이 이 단일 공유 할당 내에 맞아야 합니다. 이 경계를 초과하면 TransactionTooLargeException이 트리거되어 앱이 비정상 종료됩니다.
일반적이지만 문제가 있는 완화 전략에는 WebView 상태 번들의 크기를 모니터링하고 임의의 안전 임계값 (예: 300KB)을 넘으면 WebView 기록을 완전히 지우는 것이 포함됩니다. 이렇게 하면 비정상 종료를 방지할 수 있지만 사용자 환경에 심각한 회귀가 발생합니다.
뒤로 탐색 손실: Android는 다른 작업의 메모리를 회수하기 위해 백그라운드 앱 프로세스를 종료하는 경우가 많습니다.
onSaveInstanceState()수명 주기 콜백 내에서saveState(Bundle)을 사용하여 탐색 기록을 보존할 수 있습니다. 1MB 거래 한도를 피하기 위해 이 기록을 삭제하면 전체 탐색 스택이 손실됩니다. 사용자가 앱으로 돌아가면 프로세스 재시작이 발생했는지 여부와 관계없이 뒤로 탐색을 지원하는 기록 컨텍스트가 남아 있지 않으므로 시스템 뒤로 버튼이 즉시 구성요소 또는 앱을 종료합니다.BFCache 무효화: 기록을 지우면 앱에서 뒤로-앞으로 캐시 (BFCache)를 사용할 수 없게 되어 이전에 방문한 페이지를 즉시 렌더링할 수 없게 됩니다.
지연 시간 증가: 사용자는 WebView 내에서 현재 상태를 잃게 되므로 전체 재탐색 및 재초기화가 필요합니다. 이 프로세스는 네트워크 오버헤드와 트랜잭션 지연 시간을 크게 늘립니다.
아키텍처 완화 전략
전체 기록 삭제를 통해 사용자 환경을 저하하지 않고 TransactionTooLargeException 비정상 종료를 방지하려면 상태 보존과 메모리 효율성 간에 엄격한 균형을 유지해야 합니다. 다음 최적화 전략을 구현하면 필수 탐색 기록과 세션 무결성을 유지하면서 1MB 트랜잭션 예산을 안전하게 관리할 수 있습니다.
상태 직렬화에 크기 제한 적용
탐색 스택이 너무 커질 때 완전히 삭제하는 대신 기록 데이터를 자르는 것이 더 효과적인 패턴입니다.
타겟 삭제 정책:
WebViewCompat.saveState()를 사용하여 특정 바이트 한도를 적용하면서 상태를 직렬화합니다 (예:WebViewCompat.saveState(webView, outState, maxSizeBytes)). 이 API는 총 페이로드가 정의된 할당 내에 맞을 때까지 이전 탐색 항목을 순차적으로 자동으로 삭제합니다. 중요한 점은 이렇게 하면 활성WebView의 실시간 기록을 수정하거나 지우지 않고 직렬화된Bundle만 잘라내므로 즉각적인 뒤로 탐색이 완전히 그대로 유지된다는 것입니다.앞으로 항목 삭제: 애플리케이션 인터페이스에 뒤로 버튼이 있지만 전용 앞으로 탐색 버튼이 없는 경우 모든 앞으로 탐색 항목을
saveStateAPI의includeForwardState매개변수를false로 설정하여 삭제할 수 있습니다. 이렇게 하면 사용 가능한 탐색 경로에 영향을 미치지 않고 페이로드 크기가 크게 줄어듭니다.
HTTP Cache Quota API로 리소스 지연 시간 관리
saveState는 임시 탐색 기록의 1MB Bundle 한도를 관리하지만 HTTP Cache Quota API는 프로필별로 지속되는 웹 리소스 (디스크 캐시)를 수동으로 제어할 수 있도록 합니다. 이렇게 하면 단기 탐색 컨텍스트와 장기 캐시된 애셋이 명확하게 구분됩니다.
적절한 할당량을 선택하려면 성능 절충안이 필요합니다.
- 할당량이 높을수록 더 많은 애셋을 디스크에 보관하여 오프라인 가용성과 리소스 로드 지연 시간이 개선됩니다.
- 할당량이 낮을수록 앱의 디스크 공간이 최소화되고 OS에서 다른 중요한 앱 데이터의 캐시를 삭제하는 것을 방지할 수 있습니다.
이러한 설정은 앱 재시작 간에 유지되며 기본 스레드에서 구성해야 합니다.
다음 구현에서는 기본 프로필의 디스크 캐시 할당량을 구성하는 방법을 보여줍니다.
Kotlin
if (WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE) &&
WebViewFeature.isFeatureSupported(WebViewFeature.HTTP_CACHE)) {
val defaultProfile = ProfileStore.getInstance()
.getOrCreateProfile(Profile.DEFAULT_PROFILE_NAME)
val httpCache = defaultProfile.httpCache
// Set explicit cache size to 50MB (50 * 1024 * 1024 bytes)
httpCache.setQuotaBytes(50L * 1024 * 1024)
}
자바
if (WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE) &&
WebViewFeature.isFeatureSupported(WebViewFeature.HTTP_CACHE)) {
Profile defaultProfile = ProfileStore.getInstance()
.getOrCreateProfile(Profile.DEFAULT_PROFILE_NAME);
HttpCache httpCache = defaultProfile.getHttpCache();
// Set explicit cache size to 50MB (50 * 1024 * 1024 bytes)
httpCache.setQuotaBytes(50L * 1024 * 1024);
}
할당량 크기 조정 전략, 수명 주기 관리, 프로필 경계에 관한 자세한 내용은 WebView에서 HTTP 캐시 할당량 관리를 참고하세요.
주요 성능 고려사항
다음은 WebView 상태 동작을 제어하는 기술적 제한사항과 내부 데이터 동작을 강조합니다.
불투명
PageStateblob:saveState에서 저장하는 데이터의 약 70% 는 렌더링 엔진의 내부PageStateblob으로 구성됩니다. 이 데이터는 양식 입력 및 iframe 스크롤 위치를 비롯한 세분화된 세션 상태를 캡처합니다. 이렇게 하면 심각한 보안 위험이 발생하고 세션 복원 무결성이 손상되므로 이러한 blob에서 개별 세그먼트를 수동으로 파싱하거나 삭제하지 마세요.세분화된 기록 관리: 표준
WebBackForwardListAPI는 개별 기록 요소의 임의 삭제를 기본적으로 지원하지 않습니다. 엄격한 상태 관리를 위해서는WebViewCompat.saveState()내에서maxSizeBytes및includeForwardState매개변수를 사용하여 자르기 전략을 구현하여 아키텍처 안전성을 보장해야 합니다.