안녕하세요. 최근 Android 16 업데이트 기기(특히 삼성 One UI 8.0 기반 기기)에서 Cold Start 시 WebView 초기화 단계에서 앱이 즉시 종료(FC)되는 크래시 이슈를 마주했습니다.
Play Console Vitals 상에서 크래시 지수가 평소 대비 약 150,000% 급등할 정도로 심각했던 사안이었는데요. 앱 자체의 코드 변경이 없었던 상황에서 어떤 가설을 세우고 원인을 추적했는지, 그리고 제조사에 기술 문의를 하고 앱 레벨에서 방어 코드를 작성한 과정을 공유합니다.

Android 16 업데이트 기기에서 프로세스가 새로 시작되는 Cold Start 시점에 앱이 즉시 종료되는 현상이 감지되었습니다.
android.app.ActivityThread.performLaunchActivity 단계에서 InflateException 및 NotFoundException 발생처음 로그를 파악했을 때 ResourcesImpl 리다이렉션 키워드가 눈에 띄었습니다. 앱 코드는 동일한 상태인데 OS 버전이나 기기 환경에 따라 현상이 갈렸습니다. 그래서 내부 로직 결함보다는 OS 프레임워크와 리소스 매핑 쪽에 가설을 세우고 접근했습니다.
구글과 제조사에 기술 문의를 해서 확인한 정확한 메커니즘은 이렇습니다.
Violation) 현상이 발생합니다.ResourcesImpl 리다이렉션이 실패하면 예외 처리 없이 프레임워크 단에서 앱 프로세스를 즉시 크래시(FC) 처리해 버리는 것이 문제의 본질이었습니다.문제 포착부터 제조사 소통, 내부 데이터 검증까지의 대처 흐름입니다.
dumpstate 로그를 함께 제출해 기술 문의를 구체화했고 플랫폼 단의 리소스 매핑 결함이라는 최종 피드백을 확인받았습니다.제조사 펌웨어 업데이트로 근본적으로 해소되는 문제입니다. 다만 사용자의 OS 업데이트 지연이나 파편화 환경을 고려해 앱 레벨에서 안전하게 구동될 수 있도록 방어 코드를 추가했습니다.
Android 16에서는 GMS가 업데이트되면 GMS 패키지 경로가 랜덤하게 바뀝니다. 그런데 앱 프로세스는 이 바뀐 경로를 찾지 못한 채로 WebView 내부에서 GMS 리소스를 참조하다가 오류가 납니다.
그래서 Application 클래스의 onCreate() 시점에 createPackageContext로 GMS 패키지를 한 번 다시 로드합니다. GMS의 최신 패키지 경로를 앱 프로세스에 반영하려고 부르는 것이라 이 호출이 돌려주는 Context는 따로 쓰지 않습니다. 호출만 해도 경로가 재로드되기 때문입니다.
@SuppressLint("CheckResult")
override fun onCreate() {
super.onCreate()
initGmsPackageDependency() // GMS 의존성 컨텍스트 갱신 유도
// ..기존 초기화 로직
}
/**
* Android 16 대응 GMS 패키지 의존성 강제 등록
* 런타임 리소스 참조 위반(Violation)으로 인한 크래시 방지용 방어 코드
*/
private fun initGmsPackageDependency() {
// Android 16(SDK 36) 환경에서만 작동하도록 제한
// VERSION_CODES.BAKLAVA = 36 (compileSdk 36 이상 필요). VANILLA_ICE_CREAM(V)은 35, 즉 Android 15입니다.
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.BAKLAVA) {
DebuggingLog.d("[Application] GMS 의존성 등록 불필요 - SDK: ${Build.VERSION.SDK_INT}")
return
}
try {
// GMS 패키지 명을 기반으로 컨텍스트를 강제 로드하여 컨텍스트 갱신 유도
// 반환값은 사용하지 않음: 바뀐 GMS 패키지 경로를 재로드하는 것이 목적
createPackageContext(
"com.google.android.gms", // GMS_PACKAGE_NAME
CONTEXT_IGNORE_SECURITY or CONTEXT_INCLUDE_CODE
)
DebuggingLog.d("[Application] GMS 패키지 의존성 등록 성공")
} catch (e: Throwable) {
DebuggingLog.d("[Application] GMS 패키지 의존성 등록 실패: ${e.message}")
}
}
처음 공개한 코드에는 이 분기가
Build.VERSION_CODES.V로 되어 있었습니다.V는 Android 15(SDK 35)라서 주석과 달리 Android 16 기기에서는 방어 코드가 실행되지 않습니다. 같은 코드를 쓰고 계셨다면BAKLAVA로 바꿔 주세요.
새 안드로이드 플랫폼이 나올 때마다 리소스/보안 정책도 바뀌기 때문에 이번처럼 앱 코드를 건드리지 않았는데도 예측하기 어려운 사이드 이펙트가 생기곤 합니다.
이번 이슈에서는 원인 불명의 크래시 앞에서 정확한 로그(dumpstate) 분석과 기술 문의로 플랫폼 단의 변화를 빠르게 캐치했고 앱 레벨 방어 코드도 추가했습니다. targetSdk 36 전환이나 Android 16 환경에 대응하고 계신 개발자분들께 작은 도움이 되었으면 좋겠습니다.