Skip to content

Android:NDK:Sample

Android NDK Samples.

Windows에서 cygwin의 bash를 사용한 컴파일 방법

컴파일을 위하여 우선, 아래의 환경변수가 설정되어 있어야 한다.

  • CYGWIN_HOME: cygwin이 설치된 디렉터리.
  • NDK_HOME: NDK가 설치된 디렉터리. (반드시 PATH 변수에 추가해야 한다.)
%CYGWIN_HOME%\bin\bash.exe --login -c "ndk-build -C '${build_project}'"

Android.mk의 기본 구조

Makefile에서 읽는 Android.mk의 기본형은 아래와 같다.

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 여기 이후에 추가 설정 정보를 입력해야 한다.

# LOCAL_DEFAULT_CPP_EXTENSION := cpp
LOCAL_MODULE := [LIBRARY_NAME]
LOCAL_SRC_FILES := [SRC_FILES]

# 여기 이전에 추가 설정 정보 입력을 완료해야 한다.
include $(BUILD_SHARED_LIBRARY)
  • $(LOCAL_MODULE): 출력되는 라이브러리 파일의 명칭.
    (파일이름은 접두사로 lib, 접미사로 .so가 붙는다.)
  • $(LOCAL_SRC_FILES): 컴파일 해야 하는 소스파일 리스트.

Android.mk Macro List

Makefile에서 읽는 Android.mk에 미리 지정되어있는 매크로는 아래와 같다.

  • $(NDK_PROJECT_PATH): ndk 프로젝트 경로.
  • $(NDK_APP_APPLICATION_MK): ndk-build가 읽을 *.mk 파일을 지정한다.

Android.mk에 C/C++사용여부 추가

아래와 같이 CPP 파일 확장자를 추가해야 한다. 주의할 점은 include $(CLEAR_VARS)이후에 삽입해야 한다. (Cygwin에서 직접 컴파일 할 경우 c는 gcc, cpp는 g++로 컴파일한다.)

LOCAL_DEFAULT_CPP_EXTENSION := cpp

Java class에 JNI Library를 연결하는 방법

JNI 라이브러리를 연결하고자 하는 클래스에 아래와 같이 System.loadLibrary()를 사용하여 라이브러리를 불러온다. (주의할 점은 라이브러리 이름은 전두사 lib와 접미사 .so를 제거한, Android.mk의 $(LOCAL_MODULE)과 동일한 이름을 줘야 한다.)

package com.example;

public class TestNdkActivity extends Activity {
  static {
    System.loadLibrary("libraryname");
  }
}

메서드(Method) 연결방법

Java에서 JNI의 C함수를 사용할 때, 아래와 같이 native 키워드를 추가해야 한다.

private native String getJniTestString();

주의할점은 네이티브(Native)코드 즉, C언어측에서 함수명을 정확히 적어야 하는데, 아래와 같은 조건으로 함수명을 적어야 한다.

  • C++를 사용할 경우 extern "C"를 사용하여 C언어로 만들어야 한다.
  • C++를 사용할 경우 반환형 앞에 JNIEXPORT를 추가해야 한다.
  • C++를 사용할 경우 반환형과 함수명 사이에 JNICALL를 추가해야 한다.
  • 함수명은 아래와 같은 순서로 입력한다.
    1. 접두사로 Java_를 입력한다.
    2. 패키지명을 차례대로 입력하되, 온점(.)밑줄()로 변환한다. (패키지명에 밑줄()또는 붙임표(hyphen; -)를 사용하면 안된다.)
    3. 사용할 클래스명을 입력한다. (패키지명과 구분하기 위해 앞에 밑줄(_)을 붙인다.)
    4. 사용할 메서드(Method)명을 입력한다. (패키지명과 구분하기 위해 앞에 밑줄(_)을 붙인다.)
  • 첫 번째 파라미터는 JNIEnv* env로 고정한다.
  • 두 번째 파라미터는 jobject thiz로 고정한다.
  • 세 번째 파라미터 이후는, Java에서 넘겨줄 파라미터 순서로 사용한다.

Example

이해를 돕기 위하여 한 가지 예를 들어, 아래의 조건을 만족하는 함수를 만들어보겠다.

Language(C++), Package(com.example), class(TestNdkActivity) Method(private native String getJniTestString):

#include <jni.h>

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL
Java_com_example_TestNdkActivity_getJniTestString(JNIEnv* env, jobject thiz) {
  return env->NewStringUTF("Hello, NDK!");
}

#ifdef __cplusplus
}
#endif

C와 C++ 구현부 차이점

NDK를 사용할 경우 C와 C++의 사용방법에 차이점이 있다.

C 구현부:

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

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz ) {
  return (*env)->NewStringUTF(env, "Hello from JNI !");
}

C++ 구현부:

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

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz ) {
  return env->NewStringUTF("Hello, NDK!"); 
}

Android NDK Samples 소개

NDK설치시 함께 포함되는 샘플 프로젝트에 대한 설명:

  • hello-jni: 네이티브 메서드가 구현된 공유 라이브러리에서 문자열을 불러와 어플리케이션 UI로 띄우는 예제
  • two-libs: 동적으로 공유 라이브러리를 로드하고 제공하는 네이티브 메서드를 부르는 예제. 이때 네이티브 메서드는 정적 라이브러리에 구현되어 있고, 공유 라이브러리에 임포트 되어있다.
  • san-angeles: 네이티브 OpenGL ES의 API를 써서 3D 그래픽을 렌더링하는 예제. 렌더링 중에는 엑티비티 라이프사이클이 GLSurfaceView Object에 의해 관리된다.
  • hello-gl2: OpenGL ES 2.0의 버택스 & 프레그먼트 셰이더를 이용해 삼각형을 렌더링하는 예제.
  • hello-neon: cpufeature 라이브러리로 런타임중에 CPU capabilities를 체크하는 예제 (단, CPU가 NEON intrinsics를 지원할 경우) 특별히 이 예제는 C버전(tiny benchmark for a FIR filter loop)과 NEON 최적화 버전 두가지로 구현되어 있다.
  • bitmap-plasma: 네이티브에서 어떻게 Android Bitmap Object의 픽셀버퍼를 접근하는지 보여주는 예제.
  • native-activity: native-app-glue 정적 라이브러리로 어떻게 네이티브 엑티비티를 만드는지 보여주는 예제.
  • native-plasma: 네이티브 엑티비티로 구현된 bitmap-plasma 예제.

See also