개발하는 두더지

[Android] Android Studio + Android.mk 환경에서 .so 파일과 jar 파일 만들어서 배포하기 / 18.07.09 수정!! 본문

Java,Android

[Android] Android Studio + Android.mk 환경에서 .so 파일과 jar 파일 만들어서 배포하기 / 18.07.09 수정!!

덜지 2017. 7. 19. 14:55

NDK 빌드하는 방법 2가지


1. ndk-build 를 사용하는 방법


 - 기존의 빌드 도구 키트

 - 사내에서 eclipse로 ndk 프로젝트를 빌드하여 jar 파일과 so 파일을 만들어서 배포했는데

   android studio에서 같은 방식으로 jar 파일과 so 파일을 만들어 내기위해 이 포스팅을 시작함.


2. cmake를 사용하는 방법


 - android studio 의 기본 빌드 도구

 - https://developer.android.com/studio/projects/add-native-code.html?hl=ko#download-ndk  





일반 프로젝트 생성


" include C++ support " 를 클릭하지 않은 채로 Next 버튼을 클릭합니다.





NDK 코드 작성 후 간단하게 동작 테스트를 진행할 것이므로


Empty Activity 를 선택합니다.








안드로이드 라이브러리 생성


메인 App에서 NDK를 만들지 않고 Anroid 라이브러리를 생성하여


NDK 모듈을 만들어보겠습니다.


프로젝트 오른쪽 클릭 > New > Module



새로 만들 것이므로 Android Library 를 선택합니다.




이름은 간단하게 hello 라고 지었습니다..




현재 HelloNDK 프로젝트에는


App을 실행할 수 있는 app 모듈과 라이브러리인 hello 모듈  2개로 나눠져있습니다.




hello 모듈에서 


src > main 폴더에 jni 폴더를 생성해 줍니다.


여기서 Android.mk, Application.mk, h, cpp 등  C/C++ 관련된 파일을 생성합니다.








ndk 설치


CMake    Gradle과 함께 작동하여 네이티브 라이브러리를 빌드하는 외부 빌드 도구입니다. ndk-build만 사용하려는 경우에는

 이 구성 요소가 필요하지 않습니다.



LLDB        Android Studio가 네이티브 코드를 디버그하는데 사용하는 디버거입니다.



NDK        Android에서 C 및 C++ 코드를 사용할 수 있도록 지원하고 네이티브 액티비티를 관리하고 물리적 기기 구성 요소 ( 예 :

  센서 및 터치 입력)에 액세스 할 수 있는 플랫폼 라이브러리를 제공하는 툴바


Tools > Android > SDK Manager를 선택하여 아래의 탭에서 3가지를 다운로드 받습니다.







javah, ndk-build 환경설정


File > Settings > Tools > External Tools 에서 javah, NDK-build, NDK-build clean 3가지 툴을 만들어줍니다.




javah 란?


javah 명령어는 네이티브 메소드를 C 헤더 파일과 C 소스파일로 구현해주는 기능을 제공한다.



$JDKPath$\bin\javah.exe


-classpath "$Classpath$" -v -jni $FileClass$


$ProjectFileDir$\라이브러리명\src\main\jni





ndk-build.cmd



$ProjectFileDir$\hello\src\main




ndk-build.cmd


clean


$ProjectFileDir$\hello\src\main







ndk 메인 클래스 생성


hello > main > java > com.sample.hello 패키지 오른쪽 클릭 > New > Java Class 생성




HelloTest.java Source Code

https://github.com/pennya/HelloNDK





HelloTest Java 파일 오른쪽 클릭 > NDK > javahello ( javah 는 다른 프로젝트에서 쓰고있어서 .. ) 클릭




현재 프로젝트가 빌드가 되어있지 않다면 아래와 같이 클래스를 찾을 수 없다는 메세지가 출력된다.




프로젝트 빌드 후에 다시 실행하면 헤더 파일을 생성했다는 메시지가 출력된다.




헤더의 내용은 아래와 같다.



com_sample_hello_HelloTest.h Source Code

https://github.com/pennya/HelloNDK




JNIEXPORT, jint 등에 마우스 포인터를 두면 아직 sdk를 못불러와서 읽을수 없는 상태이다.


( 이 상태에서도 so 파일과 jar 파일을 만들어내서 동작은 할 수는 있다. )




나중에 CPP 파일을 만들고 Build > Refresh Linked C++ Projects 를 하거나 Gradle Sync를하면 링크가 재대로 된다.









android.mk 파일 생성


Hello > main > jni 폴더 오른쪽 클릭 > New > File 생성




Android.mk 파일을 생성한다.



Android.mk Source Code

https://github.com/pennya/HelloNDK



Application.mk 파일을 생성한다.


기본적으로, NDK 빌드 시스템은 armeabi ABI에 대한 기계어 코드를 생성합니다.


이 기계어 코드는 소프트웨어 부동 소수점 연산 기능이 있는 ARMv5TE 기반 CPU에 해당합니다.


ARMv7 기반 기기에 대한 하드웨어 FPU 명령                        APP_ABI := armeabi-v7a

ARMv8 AArch64                                                            APP_ABI := arm64-v8a

IA-32                                                                          APP_ABI := x86

Intel64                                                                         APP_ABI := x86_x64

MIPS32                                                                        APP_ABI := mips

MIPS64                                                                        APP_ABI := mips64

지원되는 모든 명령 집합                                                  APP_ABI := all


같은 줄에 여러 값을 배치하고 각각의 값은 공백으로 구분하여 여러 개를 지정할 수도 있습니다


APP_ABI := armeabi armeabi-v7a x86



Application.mk Source Code

https://github.com/pennya/HelloNDK





gradle 설정


Gradle을 네이티브 라이브러리에 링크하려면 CMake 또는 ndk-build 스크립트 파일 경로를 제공해야 합니다.


앱을 빌드할 때 Gradle은 CMake 또는 ndk-build를 종속적으로 실행하고 공유 라이브러리를 APK로 패키징합니다.


Gradle은 또한 빌드 스크립트를 사용하여 Android Studio 프로젝트로 가져올 파일을 파악합니다.


아래 사진처럼 Android.mk 파일을 선택하면 최종적으로 상대 경로로 설정이 됩니다.


그리고 Application.mk 파일이 Android.mk 파일과 같은 디렉토리에 있는 경우 이 파일도 포함하게 됩니다.



프로젝트 오른쪽 클릭 또는 NDK 관련 폴더 오른쪽 클릭 > Link C++ Project With Gradle 클릭




CMake에서 ndk-build 로 변경




생성한 Android.mk 파일 선택







hello 모듈의 build.gradle 에 자동으로 생성됩니다.






헤더부분 cpp로 구현


jni 폴더 오른쪽 클릭 > New > C/C++ Source File 클릭





원하는 파일명으로 생성한다. 헤더파일과 이름을 맞추지 않아도 된다.



HelloTest.cpp Source Code

https://github.com/pennya/HelloNDK



ndk-build 하여 .so 파일 생성








라이브러리 exportJar하여 jar 파일로 생성


hello 모듈의 build.gradle 파일 하단에 아래 코드를 추가한다.



build.gradle Source Code

https://github.com/pennya/HelloNDK



동작방법은 IDE 오른쪽에 Gradle이란 탭이있다.


탭을 클릭한 후에 hello 라이브러리를 jar 파일로 만들 것 이므로


hello 파일 탭을 연다.




:hello > other > exportJar 을 더블 클릭한다.





빌드가 성공되면



hello/libs 에 hello.jar 파일이 생성된다.



디컴파일해보면 정상적으로 jar 파일이 생성됨을 확인할 수있다.


여기서 Android Library 모듈을 만든 이유가 된다.


메인 App에서 NDK 관련 코드를 만들어서 Jar 파일을 만들면 MainActivity 등 불필요한 Class까지 Jar 파일에 묶이기 때문이다.








외부 프로젝트에서 로드방법



메인 App의 libs 폴더에 hello.jar 파일을 넣어준다.



메인 App의 build.gradle에서 


compile files ('libs/hello.jar')  코드를 추가한다.





메인APP > src > main 에 jniLibs 폴더를 생성하여 so 파일을 넣어준다.


다른 이름으로 폴더를 만들면 자동으로 so 파일을 찾을 수 없다.




라이브러리 프로젝트가 아닌 jar 파일에서 참조한다.




에뮬레이터를 사용하는 경우 속도를 위해 x86 에뮬레이터를 사용하는데


Application.mk 에 x86을 추가하여 x86 버전 so 파일도 만들어서 메인 App에 붙여준다.




테스트는 간단하게 Toast를 띄우는 예제이다.




10을 보내서 10을 더해서 20이 출력됨을 확인할 수있다.





18.07.09 안드로이드 스튜디오 업데이트 이후 이슈 발생


Error while executing process C:\Users\KIM\AppData\Local\Android\Sdk\ndk-bundle\ndk-build.cmd with arguments {NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=D:\workspace\Android\MediaShell4\TMS4WebServerTest\tms4webserver\src\main\jni\Android.mk NDK_APPLICATION_MK=D:\workspace\Android\MediaShell4\TMS4WebServerTest\tms4webserver\src\main\jni\Application.mk APP_ABI=armeabi NDK_ALL_ABIS=armeabi NDK_DEBUG=1 APP_PLATFORM=android-15 NDK_OUT=D:/workspace/Android/MediaShell4/TMS4WebServerTest/tms4webserver/build/intermediates/ndkBuild/debug/obj NDK_LIBS_OUT=D:\workspace\Android\MediaShell4\TMS4WebServerTest\tms4webserver\build\intermediates\ndkBuild\debug\lib APP_SHORT_COMMANDS=false LOCAL_SHORT_COMMANDS=false -B -n}


Android NDK: INTERNAL ERROR: The armeabi ABI should have exactly one architecture definitions. Found: ''    


process_begin: CreateProcess(NULL, "", ...) failed.


많은 분들이 댓글 남겨주셨지만 안드로이드 스튜디오를 업데이트하지 않았다가 업데이트를 하면서 같은 증상이 발견되었습니다. 

저도 여러가지 시도를 했지만 빌드를 성공하여 공유하고자 합니다.


에러 증상 해결 방법

ndk n17 올라가면서 armeabi 를 지원하지 않습니다. 해결하기 위해서 gradle을 업데이트해야하고

armeabi 관련 코드를 제거해야 합니다.


gradle-wrapper.properties 에서 버전을 올려줍니다.

distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip


프로젝트 레벨의 build.gradle 에서 gradle 버전을 올려줍니다.

classpath 'com.android.tools.build:gradle:3.1.3'


[라이브러리]/build.gradle 현재 환경

compileSdkVersion 27
buildToolsVersion '27.0.3'




//task to export contents as jar
task exportJar(type: Copy) {
from('build/intermediates/bundles/debug/')
into('libs/')
include('classes.jar')
///Give whatever name you want to give
rename('classes.jar', 'TMS4WebServer.jar')
}

exportJar.dependsOn(deleteOldJar, build)

기존의 exportJar는 작동되지 않네요. 



task OneJar(type: Jar) {
from ('build/intermediates/classes/release/') {
exclude '**android/'
exclude '**/BuildConfig.class'
exclude '**/R$*.class'
exclude '**/R.class'
}
}

task RenameJar(type: Copy) {
from('build/libs')
into('libs/')
include('tms4webserver.jar')
rename('tms4webserver.jar', 'TMS4WebServer.jar')
}

exportJar 자리에 OneJar, RenameJar로 변경합니다.



assembleRelease 빌드를 합니다. ( assembleDebug 로 하면 debug메세지 출력되니 테스트할 때는 assembleDebug로 하고 

build/intermediates/classes/debug 로 변경합니다. )



exclude 를 제외한 원하는 클래스만 Jar로 묶을 수 있습니다.

그리고 RenameJar를 실행시키면 [라이브러리]/libs에 원하는 Jar파일을 구할 수 있습니다.




ndk-build 명령어를 수행하면 아래와 같은 에러가 발생합니다.


ndk-build.cmd

Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-14.    

Android NDK: WARNING: APP_PLATFORM android-14 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml. NDK binaries will *not* be compatible with devices older than android-14. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.    

C:/Users/KIM/AppData/Local/Android/Sdk/ndk-bundle/build//../build/core/setup-app.mk:79: *** Android NDK: Aborting    .  Stop.

Android NDK: The armeabi ABI is no longer supported. Use armeabi-v7a.    

Android NDK: NDK Application 'local' targets unknown ABI(s): armeabi    

Android NDK: Please fix the APP_ABI definition in ./jni/Application.mk    


Process finished with exit code 2


application.mk 파일에서 armeabi 를 제거합니다

APP_ABI := armeabi-v7a x86

그리고 다시 ndk-build를 수행하면 armeabi를 제외한 나머지 플랫폼의 so 파일이 생성됩니다.

Comments