본문으로 바로가기
728x90

 

이전 내용에 이어서 본격적으로 코드를 작성해 보도록 합시다.

1. Java 코드(*.java) 작성

Java 코드에서는 라이브러리를 불러오는 class를 작성해야합니다.

아래는 esRenderer 클래스를 만들고 native-lib라는 라이브러리를 불러오는 예시입니다.

Java에서는 System.loadLibrary 함수를 통해서 라이브러리를 불러옵니다.

이는 앱이 시작될 때 메모리로 로드됩니다. 당연히 라이브러리 파일이 없으면 에러와 함께 종료됩니다.

그리고 native라는 예약어로 라이브러리에 구현한 함수를 명시해줌으로써 호출할 수 있습니다.

당연히 리턴타입, 함수명, 파라미터 등이 JNI코드와 동일해야합니다.

 

그러면 native-lib 파일은 어떻게 만드는 것일까요?

그것은 CMake 코드에 답이 있습니다.

CMake 코드 작성을 하기 전에 우선 JNI코드부터 작성해보도록 하죠.

2. JNI 코드(*.cpp, *.h) 작성

이전 장에서 모듈 우클릭 > New > Folder > JNI Folder을 선택해서 JNI파일을 저장할 폴더를 추가하였습니다.

이 폴더 아래에 JNI코드를 추가하겠습니다.

JNI폴더 우클릭 > New > C/C++ Source File을 선택합니다. 필요시 헤더파일도 추가해도 됩니다.

그리고 아래와 같이 이름을 지정해줍니다. 파일명은 어떻게 지어주든 상관은 없습니다.

그리고 아래와 같이 stringFromJNI함수를 작성해줍니다.

보시다시피 JNI관련 내용들은 jni.h에 구현이 되어있습니다.

변수타입이라던가 함수들은 jni.h파일을 참고하시면 됩니다.

그 외적인 것은 C/C++코드와 동일합니다.

JNI함수의 대략적인 템플릿은 다음과 같습니다.

extern "C" JNIEXPORT

리턴타입 JNICALL Java_패키지명_클래스명_함수명(JNIEnv* env, jobject 또는 jclass, 추가 파라미터) {

    구현부

}

이를 C의 매크로 형태로 구현해서 가독성을 높일 수도 있습니다.

 

조금 더 깊이 있는 내용을 공부하고 싶으면 아래 사이트를 참고하시기 바랍니다.

김성철님의 JNI Tutorial(sungcheol-kim.gitbook.io/jni-tutorial/)

 

Introduction

 

sungcheol-kim.gitbook.io

3. C/C++ 코드(*.cpp, *.h) 작성

우리가 흔히 VisualStudio에서 작성하는 코드들을 추가하면 됩니다.

클래스(Class)를 정의하고 구현한 뒤에, JNI 코드에서 호출해주면 됩니다.

JNI폴더 우클릭 > New > C/C++ Source File을 선택하고,

Class 이름을 정해줍니다.

각자 원하는대로 클래스를 작성해주면 됩니다.

4. CMake 코드(*.txt) 작성

이제 가장 까다로운 부분이라고 볼 수 있는 CMake 작성입니다.

빌드에 영향을 미치는 코드라서 가장 헤멜 수 있는 부분입니다.

전체 코드는 다음과 같습니다.

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

set( CPP_FILES
        native-lib.cpp
        ESRenderer.cpp)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ${CPP_FILES} )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib}

                       android)

set 함수는 setter역할을 합니다. 빌드 옵션이나 경로를 지정할 수 있습니다.

여기서 주요한 부분은 set(CPP_FILES native-lib.cpp ESRendrer.cpp) 부분입니다.

CPP_FILES라는 변수에 cpp 파일을 등록한다고 이해하시면 됩니다.

 

그리고 이를 add_library 함수를 통해 native-lib 파일에 포함시켜줍니다.

여기서 아까의 의문이 풀립니다. 지정한 파일 이름에 맞춰 라이브러리 파일이 생성됩니다.

그러므로 반드시 Java코드에서 파일명을 일치시켜 주어야 합니다.

 

마지막으로 target_link_libraries 함수를 사용해서 필요한 내장 라이브러리들을 포함시켜줍니다.

GLESv2, EGL, jnigraphics, arcore, glm 등 여러 라이브러리들이 존재합니다.

필요에 따라서 포함시켜 주면 됩니다.

 

추가로 위에는 포함이 안되어있지만

set_target_properties함수를 통해 외부 라이브러리를 포함시키거나

link_directories include_directories명령어를 통해 필요한 폴더를 포함시켜줄 수도 있습니다.

 

자세한 CMake 작성법은 다음 사이트를 참고하시기 바랍니다.

Android 스튜디오 사용자 가이드(developer.android.com/studio/projects/configure-cmake?hl=ko)

 

CMake 구성  |  Android 개발자  |  Android Developers

CMake 빌드 스크립트를 생성하고 구성하는 방법을 알아보세요.

developer.android.com

마지막으로 수정해 주어야 할 부분이 있습니다.

바로 Android 빌드에 반드시 필요한 gradle 설정이죠.

5. Gradle 코드 작성

Android Studio에서 CMake 빌드가 되도록 build.gradle(:gles)에 다음 코드를 추가해줍니다.

그리고 build.gradle(:app)에서 지금까지 만든 라이브러리를 추가합니다.

혹시 빌드하다가 라이브러리 충돌이 발생하면,

프로젝트 내부의 build폴더를 삭제하고 아래와 같이 packagingOptions를 build.gradle(:app)에 추가합니다.

android {
    ...
    생략
    ...
    
    packagingOptions {
        pickFirst 'lib/arm64-v8a/libnative-lib.so'
        pickFirst 'lib/armeabi-v7a/libnative-lib.so'
        pickFirst 'lib/x86_64/libnative-lib.so'
    }
}

이제 MainActivity에서 Native 함수를 호출 해보겠습니다.

esRenderer.java에 직접 접근해서 stringFromJNI 함수를 호출합니다.

앱을 실행해보면 정상적으로 로그가 출력됨을 알 수 있습니다.

 

이상 Android NDK(CMake)로 JNI 개발하기였습니다.

이해가 안되거나 추가로 궁금한 부분이 있으면 댓글 달아주시기 바랍니다.

728x90