ReactNative: 'UnsatisfiedLinkError: could find DSO to load' 해결하기



(글 작성한 시점의 최신 ReactNative ver: 0.40)

기존에 존재하는 Android 프로젝트에 ReactNative를 화면 단위나 view 단위로 이식할 수 있는지를 테스트하기 위해서 ReactNative doc을 참고하며 진행중이었다. 일단 기존 프로젝트에 잘못 이식하다가 프로젝트 자체가 풍비박산 될 수 있기 때문에, 그냥 AndroidStudio에서 'New Project'하여 버튼 하나를 넣어서 그 버튼을 누르면 ReactNative 화면을 띄우도록 제작하려고 했다.

코드는 대충 이렇다. 그 외 코드는 그냥 doc에 언급된대로 생명주기 메소드별 호출 및 back key 등의 이벤트 처리만 했다.


그런데..! 해당 Activity를 띄우려하니 RuntimeException이 발생했다.

자세히 보니 해당 로그 아래에 Caused by로 UnsatisfiedLinkError가 같이 찍혀있었다. (UnsatisfiedLinkError 로그는 캡쳐를 안해놔서 같은 현상의 다른 개발자 로그를 대신 첨부하였다.)

???? DSO를 찾을 수 없다고???


열심히 검색검색하니 대충 다른 개발자들은 써드파티 라이브러리를 사용하는 기존 프로젝트에 ReactNative를 이식하는 과정에서 해당 에러가 발생하는 것 같았다.

근데 난 그냥 Hello World에 버튼 하나 있는 프로젝트인데?? 외부 라이브러리 상속받은거 전혀 없는데????? gradle을 자세히보니 안드로이드가 기본적으로 넣어주는 Appcompat 서포트 라이브러리가 있었다. 물론 혹시 몰라 그 부분도 지워봤지만 에러는 똑같았다.. ^^.. ㅠㅠ



1. 이슈 원인

일단 결론을 먼저 말하자면, ReactNative가 32비트 라이브러리만 지원하고 64비트 라이브러리는 지원하지 않기 때문에 64비트인 ARM64 장치에서는 이런 이슈가 발생한다. 이 이슈가 특정 단말에서 재현되는 현상도 그런 이유에서이다.

해당 이슈는 ReactNative 공식 github에 보고된 바 있다. 그러나 ReactNative 개발자인 kmagiera는 64비트를 지원할 생각이 없어보인다. (개발자들은 애가 타고 있다.)

- kmagiera의 answer
Speaking of split builds I don't think Google's recommendation apply in the case when 80% of the APK size comes from native libs. Don't have exact values with me at the moment but from what I remember JSC lib is around 2.3M for armv7 and 2.5M for x86 (uncompressed) which adds around 1.3M to the APK size. Considering the fact that arm64 are quite rare and all of them can run armv7 binaries (JSC with JIT enabled doesn't work with libhoudini if I recall correctly from our experiments, so I consider x86 android devices incapable of running armv7 in this case) I don't think we should default to armv7+arm64+x86 builds. My suggestion would be still to default to x86+armv7 but provide config for split builds for arm64 once it's supported


이슈 원인을 보다 정확히 정리한 개발자가 있다. Android는 32비트와 64비트의 네이티브 라이브러리를 동시에 로드할 수 없다. 64비트 라이브러리는 64비트 참조 공간에 생기고, 32비트 라이브러리는 32비트 참조 공간에 생긴다. dependency 걸려있는 모든 라이브러리들이 32비트만을 지원하는 라이브러리일 경우에는 64비트 참조 공간이 생기지 않기 때문에 디바이스들은 32비트 참조 공간만을 바라보고 사용하게 된다. 그러나 만약 ARM64를 지원하는 dependency가 최소한 하나 이상 있으면 64비트 참조 공간이 생성되는데, (대부분의 라이브러리들이 32비트와 64비트를 모두 지원하지만) ReactNative같이 32비트만 지원되는 라이브러리는 64비트 참조 공간에 로드되지 않는다. 따라서 ARM32 장치들은 64비트 참조 공간이 있든 없든 32비트 주소를 바라보기 때문에 문제가 되지 않지만, ARM64 장치들은 64비트 참조 공간에 ReactNative 라이브러리가 없기 때문에 DSO를 로딩할 수 없다는 UnsatisfiedLinkError 에러를 뱉어내는 것이다.

이에 대해 kmagiera는 ARM64 디바이스는 소수라고 이야기했지만, 사실 2017년 현재까지 출시된 대부분의 최신 폰은 거의 ARM64 디바이스들이다 (...)



2. 해결 방법

ReactNative가 32비트 라이브러리를 지원해주기 전까지의 해결책은 일단 64비트 바이너리를 제외하는 것이다. 64비트 참조 공간이 만들어지지 않으면 어차피 32비트 바이너리만은 바라볼테니까 (..) 물론 이 것은 perpect solution이 아니라는 것을 명심해야 한다.

  1. 일반적인 방법으로 build 해서 apk를 추출한다. (app\build\outputs\apk\app-debug.apk)
  2. apk 파일의 압축을 풀고 lib\arm64-v8a 폴더가 있는지 본다. (사실 없을 경우 UnsatisfiedLinkError가 발생하지 않는다.)
  3. 프로젝트 최상위 루트에 있는 gradle.properties 파일에 android.useDeprecatedNdk=true 를 추가한다.
  4. app\build.gradle에 아래와 같이 코드 블럭을 추가하고, packagingOptions 블럭 안에 2번의  lib\arm64-v8a 폴더 안에서 발견된 .so 파일들의 이름을  exclude "name.so" 와 같이 나열해주면 된다.

따로 ndk를 설치하거나 할 필요 없으며, 4번까지 모두 마치고 다시 빌드하면 앱이 죽지 않고 정상적으로 실행 되는 것을 볼 수 있다. 그리고 사실 4번에서 일일이 .so 파일의 이름을 명시해주지 않아도 하기 방법으로 가능하다고 한다. (그러나 난 안된다..ㅎㅎ..)


여튼 이 문제를 ReactNative 자체에서 해결을 해줄지 모르겠지만, 일단 해결되기 전까지는 32비트 바이너리만 생성하도록 명시 해주는 것 외에 적당한 솔루션이 없어보인다. 그래도 최근 ARM64 디바이스의 증가에 따라 많은 개발자들이 이 문제를 다시 이슈화하고 있기 때문에 ReactNative에서 지원을 해줄 것이라고 희망을ㅠㅠ 갖고 싶다.

댓글

이 블로그의 인기 게시물

5년차의 슬럼프

10대 여고생이 만난 프로그래밍 - 마이크로소프트웨어 31주년 컨퍼런스

About Me