[안드로이드] JNI(Java Native Interface)에 대해 1

1. 자바와 네이티브 코드

안드로이드에서 JNI(Java Native Interface)는 자바, 코틀린에서 컴파일하는 바이트코드가 C/C++로 작성된 네이티브 코드와 상호작용하는 방법을 정의한다. 자바 언어와 자바언어가 아닌 코드가 상호작용할 수 있도록 하는 프레임워크이다.

 

자바는 플랫폼 독립적인 언어이지만, 자바로 모든 기능을 구현하기엔 어렵기 때문에 네이티브 코드와의 상호작용이 필요하다. 자바가 성능 면에서 부족한 경우, 운영체제나 하드웨어에 특화된 기능을 사용해야할 때, 이미 잘 검증된 네이티브 라이브러리를 재사용함으로써 개발 시간을 단축할 수 있다. 하지만 단점도 존재하는데, 자바와 네이티브 코드 간의 상호작용은 복잡하며, 개발자가 직접 메모리 관리와 같은 저수준 작업을 신경써야 한다.

 

 

2. 안드로이드에서 JNI 사용법

자바에서 네이티브 라이버리를 만들기 위해서는 javah로 자바 클래스 파일을 사용해 헤더를 뽑아내야 한다. JNI 네이티브 코드에서 java 클래스 파일을 찾아 해당 클래스에 있는 네이티브 메서드 선언에 메서드 정의를 바인딩할 수 있다.

 

먼저 기존 자바의 JNI 개발 순서는 다음과 같다.

  1. Java 파일 작성: 자바 클래스에서 네이티브 메서드를 선언한다.
  2. Java 파일 컴파일: 'javac'를 사용하여 자바 파일을 컴파일한다.
  3. 헤더 파일 생성: 'javah'를 사용하여 C/C++용 헤더 파일을 생성한다.
  4. C/C++ 코드 작성: 네이티브 메서드를 구현하는 C/C++ 코드를 작성한다.
  5. 네이티브 라이브러리 작성: C/C++ 코드를 컴파일하여 공유 라이브러리(`dll`, `.so`, `dylib`)를 생성한다.
  6. 실행: 자바 애플리케이션을 실행하고 네이티브 라이브러리를 로드하여 네이티브 메서드를 호출한다.

 

안드로이드 JNI 개발 순서는 다음과 같다.

 

  1. Java 파일 작성
  2. Java 파일 컴파일
  3. 헤더 파일 생성: 'javah' 대신, Android Gradle 빌드 시스템을 사용하여 C/C++용 헤더 파일을 생성한다.
  4. C/C++ 코드 작성: 네이티브 메서드를 구현하는 C/C++ 코드를 작성한다.
  5. 네이티브 라이브러리 작성: CMake 또는 ndk-build를 사용하여 C/C++ 코드를 컴파일하고 네이티브 라이브러리(`.so`)를 생성한다.
  6. 실행: 안드로이드 애플리케이션을 실행하고 네이티브 라이브러리를 로드하여 네이티브 메서드를 호출한다.

 

 

안드로이드 JNI 개발에서의 주요 차이점으로는 Gradle 빌드 시스템을 사용하며, 헤더 파일(javah)을 생성하지 않는다.안드로이드에서는 네이티브 라이브러리를 .so 파일로 빌드하고 이는 APK 파일에 포함되어 배포된다. 안드로이드에서는 JNI_OnLoad 함수를 사용하여 네이티브 라이브러리가 로드될 때 초기화 작업을 수행한다. 이 함수는 네이티브 라이브러리가 로드될 때 자동으로 호출된다.

 

 

3. 안드로이드 JNI 개발 예제

먼저, Java 파일을 작성한다.

public class MyActivity extends Activity {
    static {
        System.loadLibrary("native-lib");
    }

    public native String stringFromJNI();
}

 

네이티브 코드(C/C++)를 작성한다.

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
    JNIEnv* env,
    jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    // 초기화 작업 수행
    return JNI_VERSION_1_6;
}

 

CMakeList.txt 작성

cmake_minimum_required(VERSION 3.4.1)

add_library(native-lib SHARED
            src/main/cpp/native-lib.cpp)

find_library(log-lib
             log)

target_link_libraries(native-lib
                      ${log-lib})

 

 

빌드를 위해 Gradle을 설정한다.(build.gradle)

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

 

 

4. JNI와 네이티브 코드의 상호작용

앞서 안드로이드에서 네이티브 코드를 작성하고 빌드하는 과정을 살펴보았다. 좀 더 자세히 들어가서 JNI를 이용하여 네이티브 코드와 상호작용하는 과정을 알아본다. 빌드 과정에서 예시로 들었던 코드를 기반으로 안드로이드 앱이 실행될 때의 함수 호출 단계, JVM에서 처리하는 과정, 네이티브 코드에서 자바로 결과를 반환하는 과정을 알아본다. 다음 포스트에서.

반응형