개발 관련/Android

안드로이드 스튜디오에서 NDK 로 C++ 빌드하기 (Hello World 예제)

snoworca 2015. 3. 19. 11:30

아래 내용은 mac 기준으로 작성 되었습니다. 



1. 안드로이드 개발자 사이트에서 NDK 를 받아야 하지만, STL 과 Boost 를 사용하기 위해서 별도의 작업을 더 해야한다. 

    하지만, CrystaX 를 사용하면 쉽게 적용할 수 있다. 아래 사이트에서 최신 버전의 CrystaX 를 다운받고 압축을 푼다. 

    용량이 큰 것으로 받는다. 압축을 풀면 5GB 가 넘는다... 


   https://www.crystax.net/en/download 

  




2. 프로젝트를 생성한다.

    



3. 프로젝트의 app/src/main 폴더 안에 jni 폴더를 생성한다.


4. 아래 과정은 최초 한 번만 진행하면 된다.

       (아래 내용은 http://blog.burt.pe.kr/ 에서 참고하였습니다.)


     - javah 설정하기. 

    (1)  AndroidStudio -> Preferences 에서 External Tools 를 검색.

           (2)  External Tools 메뉴 좌측 하단의 + 버튼을 누른다. Edit Tool  다이얼로그 창이 출력된다. 

           (3)  모든 체크 박스를 체크하고, 각 입력창에 다음과 같이 적는다.

  

    Name : javah    Group : NDK    

    Description : javah   

   (모든 체크박스 체크)

    Program : /usr/bin/javah (javah 의 실행 경로를 넣는다.)

    Parameters :  -classpath $Classpath$ -v -jni $FileClass$  

    Working directory : $SourcepathEntry$/../jni

    OK 버튼 클릭. 

 


     - ndk-build 설정하기.  마찬가지로 위 과정의 (1)(2)(3) 번을 반복하고, 출력되는 Edit Tool 다이얼로그 창에 다음과 같이 적는다.  


    Name : ndk-build    Group : NDK    

    Description : ndk-build   

   (모든 체크박스 체크)

    Program :  (1번 과정에서 받은 crystax 의 경로)/ndk-build 

    Parameters :  (공란)

    Working directory : $ProjectFileDir$/app/src/main

    OK 버튼 클릭. 

    

          ndk-build 는 AndroidStudio -> Preferences 의  keymap 에서 단축키를 설정하면 편하게 사용할 수 있다.


-  위와 똑같은 방법으로  ndk-build clean 을 만들어 준다. 

    Name 항목을 ndk-build clean ,  Parameters 항목에  clean 을 추가해 주고 나머지는 위 ndk-build  설정과 동일하게 입력.


    Name : ndk-build  clean  Group : NDK    

    Description : ndk-build clean  

   (모든 체크박스 체크)

    Program :  (1번 과정에서 받은 crystax 의 경로)/ndk-build 

    Parameters :  clean

    Working directory : $ProjectFileDir$/app/src/main

    OK 버튼 클릭. 

  


5. 다음과 같은 클래스를 생성한다. 


HelloNDK.java :

public class HelloNDK {
    static {
        System.loadLibrary("hello");
    }
    public native String getHelloNDKString();
}


안드로이드 자바코드에서 NDK 라이브러리 사용을 위하여 build.gradle 파일을 다음과 같이 편집한다. 

HelloNDK 클래스에  System.loadLibrary 메소드를 통하여 입력한 모듈 이름을  moduleName 에 동일하게 입력해야 한다. 


build.gradle:

android { //...생략 defaultConfig { //...생략 ndk { moduleName "hello" } sourceSets.main { jni.srcDirs = [] jniLibs.srcDir 'src/main/libs' } }



  clean 또는 cmd + F9 (윈도우에서는 control + F9) 를 통하여 빌드한다. 


6. 앞의 4번의 과정을 정상적으로 진행하였다면 HelloNDK 클래스를 선택하고 마우스 오른쪽 버튼을 눌렀을 때 아래와 같이 나온다.

    (아래 스샷에서는 ndk-build clean 항목이 실수에 의해서 빠져있다. 참고바람)  메뉴 박스에서 NDK->javah 를 선택한다. 






Run 콘솔 창에 다음과 같은 결과가 출력된다. 

만약 5번 과정에서 빌드를 하지 않았다면 에러가 출력될 것이다. 




src/main/jni 폴더에 JNI 인터페이스 파일이 생성된 것을 확인할 수 있다. 

 





7.  jni 폴더에 HelloNDK.cpp , Android.mk, Application.mk 파일을 만들어 준다. 




9. HelloNDK.cpp 파일을 다음과 같이 편집 한다. 

    javah 를 통하여 자동으로 생성되었던 kr_re_dev_hellondk_HellpNDK.h 파일을  include 하고, 

    그 곳에 정의된 함수를 구현한다. 


    JNIEnv 클래스에 대한 설명은 다음 포스팅에서 자세히 다루겠다.


HelloNDK.cpp:

#include "kr_re_dev_hellondk_HelloNDK.h"

JNIEXPORT jstring JNICALL Java_kr_re_dev_hellondk_HelloNDK_getHelloNDKString
(JNIEnv *env, jobject obj) {

jstring str = (*env).NewStringUTF("Hello World!!!"); return str; }




9. Android.mk 파일을 다음과 같이 편집.  

    LOCAL_SRC_FILES 변수를 통하여 컴파일될 파일의 경로를 정해준다. 

  

Android.mk:

LOCAL_PATH := $(call my-dir) # 현재 파일의 설정된 경로. include $(CLEAR_VARS) # LOCAL 관련 변수를 clear LOCAL_MODULE := hello # 모듈 이름, 생성되는 파일 이름을 결정. LOCAL_CFLAGS += -std=c++14 # 컴파일 플래그. 여기서는 c++14 사용. LOCAL_SRC_FILES := HelloNDK.cpp # 컴파일 되는 파일 리스트. include $(BUILD_SHARED_LIBRARY) # 동적 라이브러리로 사용. # 현재 사용하지 않는 것들은 주석처리. #LOCAL_CPP_EXTENSION := .cpp # c++ 소스의 확장자 정의, 기본 cpp. # 임의 변경 가능 #LOCAL_C_INCLUDES := # 헤더 파일의 include 경로. #LOCAL_LDLIBS := # 연결하고자 하는 링크 옵션 #LOCAL_STATIC_LIBRARIES # 링크할 정적 라이브러리 #LOCAL_SHARED_LIBRARIES # 링크할 정적 라이브러리 #include $(BUILD_STATIC_LIBRARY) # 정적 라이브러리로 사용



Application.mk:


# 모듈 이름.

APP_MODULES := hello 


# 플랫폼 설정

# ANDROID_PLATFORM := android-9

# all 로 설정하면 안드로이드에서 사용되는 모든 종류의 cpu 로 빌드.

# 앱과 디바이스 특성별로 설정해 주는 것이 좋다.


APP_ABI
:= all #arm64-v8a armeabi armeabi-v7a mips x86 x86_64




10. 이제 NDK 파일을 빌드한다.  앞서 4번 항목에서 정의한 ndk-build 기능에 단축키를 정의해 놓으면 필요한 순간마다 편리하게 빌드할 수 있다.




 정상적으로 빌드 되었을 때 콘솔에 출력되는 결과는 다음과 같다.



 

또한 /src/main/libs 경로에 각 cpu 타입별로 빌드된 동적 라이브러리가 생성된다. 

c++14 로 빌드할 경우 gnu stl 라이브러리가 기본적으로 포함되는데 용량이 1.1Mb 로 후덜덜 하다. (boost까지 추가한다면.... )

그렇기 때문에 실제 배포시 Application.mk 파일에서 안드로이드 디바이스에서 주로 사용되는 CPU로 빌드하도록 설정하여 용량을 줄이자.



특별한 기능 없다면 아래와 같이 


APP_ABI := armeabi-v7a  


사용 하거나, x86 애뮬레이터 사용시 자신의 PC 환경에 따라서 x86 이나 x86_64 를 추가할 수 있다. 

  






11. 이제 빌드된 NDK  라이브러리인 hello 모듈을 사용해보자. 

      지금까지의 여러 단계의 설정 방법에 비하여 사용방법은 매우 간단하다.

      native 로 선언되고  c++ 함수와 연결된 메소드만 호출해주면 된다.



     지금까지 만든 NDK 모듈의 동작 여부를 간단하게 확인하기 위하여 Activity 클래스의 onCreate 이벤트 메소드에 다음과 같은 코드를 추가하였다. getHelloNDKString 으로 부터 가져온 스트링 값을 토스트로 출력하도록 하였다. 


  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Context context = getApplicationContext();
        HelloNDK helloNDK = new HelloNDK();
        Toast.makeText(context, helloNDK.getHelloNDKString(), Toast.LENGTH_LONG).show();
    }

   

   

결과