회사에서 운영중인 앱을 flutter로 개발 환경을 변경하고, 스토어에 배포를 한 이후에 몰려오는 문제로 폭풍같은 기간을 보내고 있는데, 건수는 많지 않으나 유독 앱이 실행하지 않는다라는 고객 불만이 많았습니다.
crashlytics에 아래와 같은 비정상 종료 로그가 그 불만의 원인으로 추정되었습니다.
Fatal Exception: java.lang.RuntimeException
Unable to start activity ComponentInfo{xxx.xxxxxxxxx.xxxxxxxxxxxx/xxx.xxxxxxxxx.xxxxxxxxxxxx.MainActivity}: java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/base.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/split_config.xxxhdpi.apk"],nativeLibraryDirectories=[/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/lib/arm64, /system/lib64, /hw_product/lib64, /system/product/lib64]]] couldn't find "libflutter.so"
오류는 간단하게도 libflutter.so 파일을 찾을 수 없다라는 것입니다. 오류 내용 만으로는 앱이 동작하지 않는 것이 매우 당연한 것입니다. 그런데 왜 libflutter.so가 빠져 있고, 일부 사용자만 문제가 발생하는 것일까? 라는 의문에서 파고들기 시작했습니다.
당연히 구글 검색으로 시작했고, 나 뿐만 아닌 다른 사람들도 동일한 문제가 있음을 확인했습니다. 나만의 문제가 아니면 이미 해법은 있을 것이라는 가벼운 마음으로....
참고로 저희 앱은 구글 플레이 스토어를 통해서 배포하고 있고, app bundle 형태로 등록하고 있습니다.
첫번째 시도..
Fatal Exception: java.lang.RuntimeException
Unable to start activity ComponentInfo{xxx.xxxxxxxxx.xxxxxxxxxxxx/xxx.xxxxxxxxx.xxxxxxxxxxxx.MainActivity}: java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/base.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.en.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.fr.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.hdpi.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.x86.apk"],nativeLibraryDirectories=[/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/lib/x86, /data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/base.apk!/lib/x86, /data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.en.apk!/lib/x86, /data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.fr.apk!/lib/x86, /data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.hdpi.apk!/lib/x86, /data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-1/split_config.x86.apk!/lib/x86, /system/lib, /vendor/lib, /data/downloads, /data/priv-downloads]]] couldn't find "libflutter.so"
오류에서 보듯이 x86 버전에 대한 라이브러리를 찾고 있었습니다. 안드로이드 계열에 익숙하지 않아서 x86 칩을 사용하는 단말인가 싶어서 오류 발생 단말 정보를 찾아보았습니다. 당연히 ARM 계열 단말이었습니다. arm과 x86을 같이 지원하는 경우도 있나.. 그런가 보다 대충 이해하고 넘어가기로 했습니다. 그게 중요하지 않으니까요..
그럼 x86 라이브러리 만 있으면 어찌 되었던 돌아 가는 것이니까. 라이브러리가 포함되는지 여부 부터 확인을 했습니다. apk 로 빌드해서 라이브러리가 포함 여부를 확인했는데 역시 x86 폴더는 libflutter.so가 포함되어 있지 않았습니다.
찾아보니 flutter는 x86을 지원하고 있지 않았습니다. 그러니 x86 폴더는 당연히 비어 있는 것이지요.
이 문제에 대한 구글 검색에서 나오는 것들과 같이 abiFilters를 지정해서 해결하기로 결정했다. x86은 지원할 수 없으니까..
abiFilters 'armeabi-v7a','arm64-v8a'
두번째 시도..
첫번째 시도에도 불구하고, 여전히 동일한 오류가 계속 등록되었습니다. 예외 내용은 미묘하게 바뀌긴 했습니다.
Fatal Exception: java.lang.RuntimeException
Unable to start activity ComponentInfo{xxx.xxxxxxxxx.xxxxxxxxxxxx/xxx.xxxxxxxxx.xxxxxxxxxxxx.MainActivity}: java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/base.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/split_config.xxxhdpi.apk"],nativeLibraryDirectories=[/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/lib/arm64, /system/lib64, /hw_product/lib64, /system/product/lib64]]] couldn't find "libflutter.so"
이번엔 x86이 아닌 lib64였습니다. 문제는 arm64-v8a는 flutter도 지원하고 있다는 것이었습니다. 이 문제에 대한 구글에서 검색되는 해결 방법은 armeabie-v7a만 포함하라는 것이었습니다. 문제는 64비트 지원하는 단말에서 32비트를 사용하고 싶지 않았고, 플레이 스토어에 앱을 등록하려면 이제는 반드시 64비트가 포함되어야 한다는 것이었습니다.
오류가 발생하는 단말의 대체로 샤오미에 몰려 있어서 샤오미 관련 이슈인가라는 의심을 하고 있는 중에서 아래 링크를 보게 되었습니다.
https://issuetracker.google.com/issues/147096055?pli=1#comment6
그래서 해당 코멘트에서 가이드하는 방법으로 수정을 했습니다.
android.bundle.enableUncompressedNativeLibs=false
세번째 시도..
이 정도 했으면 문제가 더 이상 발생하지 않을 것도 같은데, 여전히 문제는 계속 발생했습니다.
구글 검색에서 더 이상 새로운 내용은 보이지 않고, 전체 사용자에 비해서 빈도가 높지 않아서 묻고 갈까도 고민을 했으나, 유독 고객 리뷰에 문제 제기가 많아서 묻을 수도 없어서 원점으로 돌아가서 다시 문제를 보기로 했습니다.
예외 메시지를 다시 들여다 봤습니다.
Fatal Exception: java.lang.RuntimeException
Unable to start activity ComponentInfo{xxx.xxxxxxxxx.xxxxxxxxxxxx/xxx.xxxxxxxxx.xxxxxxxxxxxx.MainActivity}: java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/base.apk", zip file "/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/split_config.xxxhdpi.apk"],nativeLibraryDirectories=[/data/app/xxx.xxxxxxxxx.xxxxxxxxxxxx-K_hBqtMu3gBO-zaDGjmAZA==/lib/arm64, /system/lib64, /hw_product/lib64, /system/product/lib64]]] couldn't find "libflutter.so"
libflutter.so 파일이 시스템에 포함된 파일은 아니기 때문에 /lib/arm64 와 같은 시스템 폴더에서 찾을 수 없는 것은 당연한 것이기 때문에 무시하고, base.apk, split_config.xxxhdpi.apk 두개 중에 어디인가에 파일이 포함되어 있어야 하는게 맞다는 소리인데 어디에 포함되어 있는게 정상일까?
app bundle로 배포될 경우에 각 단말에 맞는 형태로 앱이 변경되어 설치된다 정도만 알고 있었기 때문에 app bundle을 다시 공부해보았습니다.
간단히 요약하면 앱 번들 형태로 배포시 앱의 기본 모듈을 포함하는 base.apk, abi 별 라이브러리가 포함되는 split_config.abi.apk, 해상도별 리소스를 포함하는 split_config.dpi.apk 형식으로 나뉘어져서 배포되는 걸로 설명되어 있었습니다.
그런데 예외를 보면 base.apk와 split_config.dpi.apk 에 해당하는 것은 보이나, split_config.abi.apk는 보이지 않습니다.
내가 만든 앱 번들이 잘못되었나? 라는 의문을 갖고 확인해보기로 했습니다.
bundletool build-apks --bundle=build/app/outputs/bundle/release/app-release.aab --output=out_app.apks --connected-device --ks=password.jks --ks-pass=file:password.pwd --ks-key-alias=keyalias --key-pass=file:password.pwd
앱 번들을 연결된 단말의 형태에 맞는 apk를 생성해주는 명령어입니다.
base.apk, split_config.arm64_v8a.apk, split_config.xxxhdpi.apk 세개의 파일이 생성되고, split_config.arm64_v8a.apk에 libflutter.so 파일이 포함되어 있음을 확인했습니다.
그렇다면 내가 만든 앱 번들은 문제가 없고, 설정이나 그런 이유로 플레이 스토어에서 배포되는 과정에서 파일 누락이 발생했다는 이야기가 되었습니다. 그래서 다시 구글신의 도움을 받아 보기로 했으나 검색되는 내용으로는 어떤 특별한 유저들이 구글 플레이가 아닌 다른 경로를 통해서 앱을 설치하는 경우에 누락이 발생할 수 있다라는 이해할 수 없는 내용들이 검색되었습니다. 뭐 유저들이 그런 경로를 통해서 다운로드를 받으셨다고 치고, 그런데 그 왜 유독 abi에 해당하는 파일만 누락되며, 그렇게 다른 경로를 통해서 받으셨는데 플레이 스토어에 리뷰를 달고 계실까..
답이 없는 것으로 판단하고 그럼 이 문제를 회피할 수 없을까? apk로 전체를 배포 하면 되는데 그건 플레이스토어에서 막혀 있으니, 앱 번들이면서 apk 형태로 배포할 수 있는 방법을 없을까를 찾아 보았습니다.
역시나 방법은 있었습니다. 앱 번들 설정에서 abi를 분리하지 않도록 설정할 수 있었습니다.
bundle {
abi {
enableSplit = false
}
}
이렇게 설정을 하면 base.apk에 모든 abi 파일들이 포함되기 때문에 앱 번들로 배포하는 효과는 퇴색됩니다. (arm64_v8a 뿐만 아니라 armeabi_v7a도 함께 포함됩니다.) 라이브러리 파일들이 용량이 크면 최대 용량 초과 등의 문제를 발생시킬 수는 있습니다만, 그 정도 상황은 아니고 설치는 되었으나 앱이 안돌아가는 상황보다는 좋을 것이기 때문에 이 정도로 문제를 닫기로 했습니다.
구글 플레이 스토어의 알 수 없는 문제인 걸로 마무리를 짖습니다.
혹시나 제가 잘못 이해하고 있거나 하면 댓글로 남겨주시기 바랍니다.
추가 : 그러면 flutter로 변경하기 이전 버전에서는 문제가 없었는가를 찾아 보았습니다. 비슷한 빈도로 다른 라이브러리를 로드하지 못했다라는 예외가 발생하였으나, flutter 처럼 앱이 당장 실행되지 않는 문제는 아니기에 부각이 덜 되었던 것 같습니다.
댓글
댓글 쓰기