
androidx.pdf beta01: 격리 프로세스에서 Application.onCreate가 한 번 더 돌아 PDF가 조용히 안 열렸다
androidx.pdf의 격리 프로세스에서도 Application.onCreate가 돕니다. 첫 줄에 isIsolated 가드를 두고 테스트로 고정하십시오.
if (Process.isIsolated()) return은 한글뷰어 슈가독의 Application.onCreate 첫 줄입니다. 2026년 10월 5일 Galaxy S21(Android 15)에서 이 줄이 없는 릴리스 빌드는 PDF를 열지 못했습니다. PDF를 여는 격리 프로세스가 앱의 onCreate를 그대로 실행하다 SharedPreferences에서 죽었고, 저장된 로그 약 6.5초 동안 오류 콜백은 하나도 오지 않았습니다. 가드를 넣은 빌드는 같은 기기에서 5쪽 PDF를 81ms에 열었습니다.
근거는 SugarMount 앱 저장소(sugar-utils)의 M0 스파이크 문서 V8 절(2026-10-05), 그때 저장한 S21 logcat 두 개, 커밋 ca1ade4(2026-10-08)의 SugarDocApp.kt와 ViewerArchitectureTest.kt입니다. 기준일은 2026년 10월 21일입니다.
목차 (7)
격리 프로세스에서도 Application.onCreate가 돕니다
androidx.pdf 1.0.0-beta01은 PDF 파싱을 PdfDocumentServiceImpl이라는 서비스에서 합니다. 이 서비스는 isolatedProcess로 선언돼 있어 앱과 다른 프로세스에서 실행됩니다. 그런데 안드로이드는 그 프로세스에서도 앱의 Application 클래스를 만들고 onCreate를 부르는데, 격리 프로세스는 앱 저장소에 접근하지 못합니다.
스파이크 앱(Compose PdfViewer + SandboxedPdfLoader, R8로 줄인 릴리스)은 onCreate에서 일부러 SharedPreferences에 값을 썼습니다. 가드가 없는 빌드의 logcat은 이렇게 남았습니다.
02:35:47.092 I M0V8: APP_ONCREATE pid=1678 isolated=true guard=false proc=kr.sugar.m0v8:androidx.pdf.service.PdfDocumentServiceImpl
02:35:47.093 E M0V8: APP_INIT failed isolated=true: java.lang.IllegalStateException: SharedPreferences cannot be accessed if UserManager is not available. (e.g. from inside an isolated process)
02:35:47.093 E AndroidRuntime: FATAL EXCEPTION: main
02:35:47.110 I ActivityManager: Process kr.sugar.m0v8:androidx.pdf.service.PdfDocumentServiceImpl (pid 1678) has died
앱 프로세스는 멀쩡했고, 크래시 창도 뜨지 않았습니다. 격리 프로세스가 죽은 뒤 로그가 끝나는 02:35:53.689까지 PDF가 열렸다는 줄(V8_OPEN)은 끝내 나오지 않았고, 앱 쪽에는 실패를 알리는 콜백도 없었습니다. 약 2분 전에 같은 기기에서 돌린 가드 빌드는 격리 프로세스의 onCreate가 바로 돌아간 뒤 5쪽, 466,529바이트 PDF를 81ms에 열었습니다.

10월 4일 분석 문서에서 10월 8일 커밋까지
문서와 git 기록에 남은 순서만 옮깁니다. 중간 커밋은 없고, 아래 과정은 모두 2026-10-08 커밋 하나에 들어갔습니다.
- 10월 4일: 설계 교차 검증 문서가 PDF 샌드박스 서비스의 격리 프로세스에서도 Application.onCreate가 돈다고 적었습니다. 태그는 [검증/추정]이었습니다. 같은 날 쓴 구현 계획은 이를 확인할 스파이크 V8을 새로 만들었습니다.
- 10월 5일 02:33: S21에서 가드를 넣은 릴리스 빌드를 실행했습니다. 격리 프로세스 로그에
isolated=true guard=true가 찍혔고 PDF는 열렸습니다. - 10월 5일 02:35: 가드가 없는 빌드를 실행했습니다. 위 로그처럼 격리 프로세스가 죽고 PDF는 열리지 않았습니다.
- 10월 5일(스파이크 문서): 측정 에이전트의 보고를 검증 에이전트가 다시 계산했습니다. 무응답 시간 "8초"를 "약 6.5초"로 고친 것이 이 단계입니다.
- 10월 5일(M1 결과): 본 앱 코드에 가드를 넣고, 가드를 지우면 테스트가 실패하는지 확인했다는 기록이 남았습니다.
- 10월 8일 08:56: 커밋 ca1ade4가 앱의 M1~M5 작업 전체와 함께 가드와 아키텍처 테스트를 올렸습니다.
설계 분석 문서가 먼저 의심했고, 스파이크가 일부러 실패를 재현했습니다. (스파이크 앱은 S20에도 깔 예정이었지만, 다른 세션의 앱이 그 폰을 쓰고 있어 설치하지 않았습니다.)
첫 줄 가드는 아키텍처 테스트가 고정합니다
앱 클래스 SugarDocApp.kt에서 가드는 17행, onCreate의 첫 문장입니다. 주석은 이 줄이 첫 문장으로 남아야 한다고 적고, 지키는 쪽으로 ViewerArchitectureTest를 가리킵니다. 누군가 super.onCreate() 아래로 옮기거나 그 위에 로그 한 줄을 넣으면 테스트가 실패합니다.
override fun onCreate() {
if (Process.isIsolated()) return
super.onCreate()
container = AppContainer(this)
}
ViewerArchitectureTest는 소스 파일을 직접 읽어 onCreate 다음의 첫 문장이 정확히 이 문자열인지 비교합니다. onCreate보다 먼저 도는 코드도 함께 봅니다. 초기값이 있는 멤버 프로퍼티, init 블록, companion object, attachBaseContext가 대상이고, lateinit과 by lazy만 허용합니다. 검출기도 따로 시험합니다. 위험한 줄 5개를 넣은 예제에서 정확히 5건을 찾아야 통과합니다.
androidx.pdf를 쓰는 앱이라면 이 가드를 onCreate 첫 줄에 두고, 그 위치를 테스트로 고정하십시오. DataStore, SharedPreferences, Firebase 초기화처럼 onCreate에 쌓이는 코드는 대부분 저장소를 건드려서, 코드 리뷰로는 지키기 어렵습니다. 테스트가 정말 가드를 지키는지는 가드를 지워 실패를 보는 방식으로 확인했습니다. 방법은 테스트 3,066개가 초록인데 DB를 못 여는 코드가 숨어 있었다: 가드 지워 보기에 따로 정리했습니다.
측정 에이전트는 8초라고 보고했고, 로그는 6.5초였습니다
V8 스파이크는 AI 코딩 에이전트 둘이 나눠 맡았습니다. 측정 에이전트가 보고하면 별도 검증 에이전트가 증거 파일을 다시 열어 숫자를 새로 계산했습니다. 스파이크 문서 머리말에는 "이 문서의 수치는 검증 에이전트가 정정한 값이다"라고 적혀 있습니다.
가드 없는 빌드의 무응답 시간을 측정 에이전트는 8초로 보고했습니다. 검증 에이전트는 logcat을 다시 열어 "약 6.5초"로 고쳤습니다. 이 6.5초는 저장된 로그의 길이이고, PDF는 그 안에서 끝내 열리지 않았습니다. 글을 쓰며 다시 세어 보니 프로세스가 죽은 02:35:47.110부터 마지막 줄까지 6.6초였습니다.
같은 절에서 검증 에이전트가 고친 항목은 7개였습니다. 문서 열기 시간은 183/99ms가 81/59ms로, 검색 시간은 95~211ms가 286/64ms로 바뀌었습니다. 스크롤 확인 증거는 무효로 돌렸습니다. 1쪽과 마지막 쪽 캡처의 md5가 같았기 때문입니다. 틀린 쪽도, 고친 쪽도 AI 에이전트였습니다. 사람이 숫자를 고친 기록은 없습니다. 측정과 검증을 다른 에이전트에 맡기고, 문서에는 정정한 값만 남깁니다.
격리 서비스가 죽는 경로는 하나가 아니었습니다
가드는 프로세스가 onCreate에서 죽는 경로 하나만 막습니다. 격리 서비스는 다른 이유로도 죽습니다. 그래서 리더 쪽 ReaderController가 openDocument에 8초 타임아웃(PDF_OPEN_TIMEOUT_MS = 8_000L, 화면에 보인 시간만 셈)을 걸고, 시간이 지나면 오류 화면(E-코드)으로 끝냅니다. M4 점검에서 openDocument 도중 am crash로 서비스를 죽였더니(3회 × 2기기) 결과는 E14(다시 시도)이거나 서비스에 다시 붙어 첫 쪽을 그리는 것이었고, 모두 0.5~2.4초 안에 끝났습니다.
더 아픈 경로는 문서가 열린 뒤였습니다. 서비스가 죽으면 androidx.pdf의 PdfView가 Material 오류 Snackbar를 띄웁니다. 그 레이아웃의 ThemeOverlay.AppCompat.Dark가 플랫폼 테마에 없는 ?attr/colorAccent 등을 읽다가 InflateException을 냈고, 이번에는 앱 프로세스가 죽었습니다. 리더 테마에 AppCompat 색 속성 7개를 넣어 고쳤고, 부모 테마는 플랫폼 테마 그대로 뒀습니다. 고친 뒤 S21에서 9회 돌려 앱 크래시는 0이었습니다.
R8 keep 규칙은 하나도 더하지 않았는데 릴리스에서 동작했습니다. APK는 1,263,741바이트에서 2,640,995바이트로, 1,377,254바이트 늘었습니다(dex +666,472, arsc +532,888, res +134,546). 같은 앱의 HWP 엔진 측정은 안드로이드 한글 뷰어 만들기: rhwp + WebView를 문서 222개로 실측한 기록에 있습니다.

다시 한다면
무응답 시간을 로그 길이로 재지 않을 겁니다. 스파이크 앱에 "openDocument 호출 시각"과 "타임아웃 발생 시각"을 직접 찍는 줄을 넣었다면 8초와 6.5초를 두고 다툴 일이 없었습니다.
콜백 없는 8초 타임아웃 경로도 기기에서 먼저 재현할 방법을 만들어 두겠습니다. 지금은 JVM 테스트로만 확인했습니다. 릴리스 빌드에서는 격리 프로세스를 멈춰 세울(SIGSTOP) 방법이 없었기 때문입니다. 전이 의존도 스파이크에서 미리 보겠습니다. V8 증거에는 의존성 트리가 없어 [미확인]으로 남았고, androidx.pdf가 appcompat 1.7.0·fragment 1.5.4·material 1.13.0을 끌고 온다는 사실은 같은 날 M1에서 본 앱을 만들며 알았습니다. 다음 스파이크에서는 ./gradlew :app:dependencies 출력을 처음부터 증거 폴더에 넣겠습니다.
자주 묻는 질문
androidx.pdf에서 PDF가 오류 없이 안 열리면 무엇부터 보나요?
격리 프로세스 이름(:androidx.pdf.service.PdfDocumentServiceImpl)으로 logcat을 걸러 FATAL EXCEPTION이 있는지 봅니다. Application.onCreate의 저장소 초기화가 그 프로세스만 죽이면 앱에는 콜백이 오지 않습니다.
격리 프로세스에서 SharedPreferences를 쓰면 왜 IllegalStateException이 나나요?
격리 프로세스는 UserManager를 쓸 수 없어 SharedPreferences를 열지 못합니다. 실제 메시지는 "SharedPreferences cannot be accessed if UserManager is not available. (e.g. from inside an isolated process)"였습니다. Application.onCreate 첫 줄에서 Process.isIsolated()이면 바로 return하면 됩니다.
androidx.pdf beta01에 R8 keep 규칙을 따로 넣어야 하나요?
이 앱에서는 넣지 않았습니다. 2026-10-05 S21에서 keep 규칙을 더하지 않은 R8 릴리스 빌드가 PDF 열기, 검색, 텍스트 선택까지 동작했습니다. AAR 4개에 consumer 규칙 파일이 없다는 것도 확인했습니다.
이 글은 SugarMount가 직접 측정한 값으로 쓰고, 초안 작성에 AI 코딩 에이전트를 썼습니다.
다른 AX 노트
- Android assets를 미리 gzip한 실측: APK 이득은 0, .pack.gz는 .pack이 되어 FileNotFoundException
- SKT 판매 삼성 폰에서 market://을 열면 스토어 선택 창이 뜬다: Play 패키지부터 지정하기
- addOnCompleteListener(this)가 onStop에서 지운 리스너 때문에 스플래시에 멈춘 버그
- "6.7" < "6.11"이 거짓이라 콘텐츠 갱신이 멈췄다: 6.2~6.9 기기만 막힌 Remote Config 버전 비교
- 화살표 퍼즐 1,100판을 만든 역구성 생성기와 G2 게이트
- 대개편 직후 DAU는 올랐고, 자연 유입 코호트 D1은 15.2%에서 11.2%로 내려갔다