Android 앱을 개발 중에는 잘 됐는데 배포용 APK 를 만들어 설치하니 한 화면이 안 열렸다.
Table of contents
Open Table of contents
배포본에서만 안 열렸다
adb logcat 을 보니 클래스를 못 찾는다는 오류였다.
android.view.InflateException: Binary XML file line #12:
Error inflating class com.example.CustomView
Caused by: java.lang.ClassNotFoundException: com.example.CustomView
소스에는 분명히 있고 개발 빌드에서는 잘 돌아간다.
차이는 default.properties 에 있었다. proguard.config=proguard.cfg 가 들어 있다. ProGuard 는 배포 빌드에서만 돈다. 참조가 안 보이는 클래스를 지운 것이다.
설정 파일의 이름은 참조로 안 보인다
지워진 클래스를 어디서 쓰는지 봤더니 레이아웃 XML 에 이름으로 적혀 있었다.
<com.example.CustomView ... />
Java 코드가 아니라 XML 에만 이름이 있다. ProGuard 는 아무도 안 쓴다고 본다.
기본 proguard.cfg 에 커스텀 뷰용 줄이 이미 있긴 했다.
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
그런데 이건 생성자를 지키는 규칙이다. 클래스를 지키는 규칙이 아니다. 클래스째 지워지면 생성자도 같이 간다. -keep class com.example.CustomView 를 따로 넣으니 해결됐다.
같은 이유로 지워질 것이 더 있을 것 같았다. XML 에서 이름으로 쓰는 것과 Class.forName 으로 찾는 것과 직렬화되는 것 셋이다.
돌려 보고 깨지는 것을 찾는 대신 볼 방법이 있었다. -printusage 를 켜면 ProGuard 가 지운 것이 파일로 나온다. -printseeds 는 지킨 것을, -printmapping 은 바뀐 이름을 낸다. 지운 목록에서 우리 패키지 이름을 grep 하니 남은 것이 바로 나왔다.
전 화면을 열어 봤다
그래도 APK 로 모든 Activity 를 한 번씩 열어 봤다. 목록을 만들어 하나씩 체크하며 했다. 일부만 보면 안 열어 본 화면에서 나중에 터진다.
이렇게 하니 세 개를 더 찾았고 전부 같은 이유였다.
여기서 알게 된 것은 확인하는 빌드가 달라야 한다는 것이었다. 개발 빌드로만 확인하면 배포에서 터진다. 다만 배포 빌드는 ProGuard 와 jarsigner 와 zipalign 이 붙는다. 오래 걸려서 매번 하기는 무겁다. 평소에는 개발 빌드로 작업하고 내기 전에 한 번 배포 빌드로 전 화면을 보는 것으로 정했다.
배포 빌드와 개발 빌드의 차이
두 빌드가 다른 것이 코드 축소 말고도 있었다.
로그가 다르다. Log.d 는 개발에서만 남기게 돼 있어서 APK 에서는 아무것도 안 남는다. 그러면 APK 에서 문제가 나도 조사할 것이 없다. Log.d 는 개발만 남기되 Log.e 는 APK 에서도 남기게 했다.
서버 주소도 다르다. 개발 빌드는 개발 서버를 보고 배포 빌드는 운영 서버를 본다. 개발에서 되던 것이 운영 서버에서 안 될 수 있다. 배포 전 확인에 운영 서버 접속도 넣었다.
서명도 다르다. 개발 빌드는 debug.keystore 로 서명된다. 외부 서비스가 서명 해시로 앱을 확인하는데 그 해시는 등록돼 있지 않다. 그래서 개발에서 안 되던 것이 배포에서 되는 경우도 있었다. 대부분은 개발에서 되고 배포에서 안 되는 방향이다. 일부는 반대라 양쪽을 다 봐야 했다.
배포 전 확인 목록을 만들었다. APK 로 전 화면 열기와 운영 서버 접속과 외부 연동과 Log.e 넷이다. 적어 두니 빠뜨리는 일이 없어졌다.
정리
- 배포 빌드에만 코드 축소가 켜져 있어 개발 빌드와 다르게 돈다
- 레이아웃 XML 에서 이름으로 쓰는 것은 참조로 안 보여서 ProGuard 가 지운다
- 기본
proguard.cfg의 커스텀 뷰 줄은 생성자만 지킨다. 클래스는-keep을 따로 준다 -printusage를 켜면 지워진 목록이 파일로 나온다. 돌려 보며 찾지 않아도 된다- 배포본으로 전 화면을 한 번씩 열어 본다
- 매번은 무거우니 내기 전에 한 번 한다
- 배포본은 로그가 안 남아서 문제가 나면 조사할 것이 없다
- 디버그 로그는 개발만 남기고 오류는 항상 남긴다
- 서버 주소와 서명도 다르다
- 개발에서 되던 것이 안 되는 경우와 그 반대가 다 있다