'Android NDK'에 해당되는 글 1건

728x90
전세계적으로 개발자가 자바 언어만큼 많이 사용하는 또다른 프로그램 개발 언어는 C언어와 C++언이이다.
C/C++언어는 자바와 달리 특정 플랫폼에 최적화시킬 수 있는 장점을 제공한다.
java만 사용하여 필요 기능과 성능을 모두 만족시키기는 힘들다. 안드로이드는 자바 기반의 언어다. 자바는 JVM(Java Virtual Machine)에서 동작하기 때문에 실제적인 디바이스에 접근이 불가능하다. 그래서 나온 것이 안드로이드 NDK이다.
NDK을 사용하여 디바이스에 접근 제어할 수 있는 C/C++ 함수들을 호출할 수 있도록 하여 안드로이드 개발자들이 좀 더 효율성 있게 개발할 수 있도록 도와주는 역할을 한다.

C, C++ 로 작성된 프로그램을 안드로이드에서 사용할 수 있도록 JDK에서 제공하는 것이 JNI(Java Native Interface) 이다.

Java에는 상응하는 것이 없는 수많은 C/C++ 라이브러리가 있다. 이러한 라이브러리를 이용하려면 NDK를 사용하는 것이 좋다.

안드로이드는 C/C++언어로 개발된 프로그램을 Java와 구분하기 위해 네이티브 코드라고 하며, 네이티브 코드로 작성되어 실행이 가능한 메소드를 일반 Java 메소드와 구분하여 네이티브 함수라 부른다.


안드로이드 스튜디오에서 C++ 코드를 import 하여 사용하기 위해 필요한 설정이다.

화면 캡처는 맥북에서 했다. 맥 윈도우 8.1 에서 CMake 인식이 안되어서 맥북 모드로 다시 확인하니까 맥북에서는 잘 인식되어 installed 했다.

최신 알파버전 Android Studio 4.0  을 다른 폴더에 설치하고 실행해서 보니 안보이던 cmake 선택창이 보여서 설치했더니, 이제는 Android Studio 3.5.2 에서도 잘 보인다. 흐미....



https://developer.android.com/ndk/guides?hl=ko 에 설명이 잘 되어 있다.

NDK(Native Development Kit)는 Android에서 C 및 C++ 코드를 사용할 수 있게 해주는 일련의 도구 모음으로, 네이티브 액티비티를 관리하고 센서 및 터치 입력과 같은 물리적 기기 구성요소에 액세스하는 데 사용할 수 있는 플랫폼 라이브러리를 제공한다.

- 기기에서 최대한의 성능을 도출하여 짧은 지연 시간을 달성해야 하는 경우

- 게임 또는 물리학 시뮬레이션과 같은 연산 집약적인 애플리케이션을 실행하는 경우

- 본인 또는 다른 개발자의 C 또는 C++ 라이브러리를 재사용하는 경우


ㅇCMake: Gradle과 함께 작동하여 네이티브 라이브러리를 빌드하는 외부 빌드 도구다.

   (Android Studio’s default build tool for native libraries is CMake.)

   ndk-build만 사용하려는 경우에는 이 구성요소가 필요하지 않다.

  

ㅇLLDB: Android 스튜디오에서 네이티브 코드를 디버깅하는 데 사용하는 디버거다.



C++ 코드 사용 가능한 프로젝트 생성




설치된 폴더를 확인해보면, NDKTest 라는 프로젝트명이 생겼다.


소스 폴더에 java, res 폴더 외에 cpp 라는 폴더가 추가로 생긴 걸 확인할 수 있다.


cpp 폴더안에 native-lib.cpp 파일과 CMakeLists.txt 파일 기본 생성되었다.


모든 C++ 파일과 라이브러리는 cpp 폴더 안에 있어야 한다.


cpp 파일 작성법

default 로 생성된 파일을 분석해보자.

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

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


#include <jni.h> // 다수의 매크로 정의, 타입, 구조체, 함수를 포함하는 헤더 파일로 반드시 포함해야 한다.

JNI(Java Native Interface)를 통해 접근할 수 있는 C++ 함수명은 다음과 같은 형태여야 한다.

'JNIEXPORT <함수 타입> JNICALL Java_<패키지 명>_<클래스 명>_<함수명>(JNIEnv*, jobject, 파라미터) 이다.

Java_com_link2me_android_ndktest_MainActivity_stringFromJNI(JNIEnv *env, jobject) {

Java는 접두어이고,

package="com.link2me.android.ndktest" 패키지명에서 . 대신에 _ 로 바꾼다.

자바 클래스명 : MainActivity

함수명 : stringFromJNI

JNIEnv*, jobject는 네이티브에서 자바로 접근하는 포인트가 된다.


extern "C" JNIEXPORT jstring JNICALL
Java_com_link2me_android_ndktest_MainActivity_intFromJNI(
        JNIEnv *env, jobject b, jint a, jint b) {
    // 연산결과 = 연산식
    return env->NewStringUTF(연산결과);
}


public native int sum(int a, int b);

매개변수가 추가된 경우에는 네이티브 코드에 자주색처럼 추가해주면 된다.



MainActivity.java 파일 추가 사항을 살펴보자.

public class MainActivity extends AppCompatActivity {

    // 네이티브 함수를 사용하기 위하여 동적 라이브러리를 로딩
    // 동적라이브러리는 stringFromJNI메서드가 호출되기 전에 로딩되어야 하므로 static으로 초기화
    static {
        System.loadLibrary("native-lib"); // 라이브러리명은 소문자를 사용해야 한다.
    }

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

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    // java에서 사용할 네이티브 함수의 선언
    // native라는 키워드는 이 메서드가 java가 아닌 다른 언어로 작성된 것임을 암시
    public native String stringFromJNI();
}

자바 프로그램내 선언한 메소드에 native 라는 키워드를 추가하지 않는다면, 자바 컴파일러는 메소드들이 모두 자바 언어로 작성되어 있다고 가정한다.

native 선언은 메소드는 자바 언어가 아닌 C나 C++ 언어로 작성되어 있다는 사실을 알려준다.

자바 프로그램의 특성상 메소드의 선언은 C/C++ 언어와 달리 클래스의 어디에 위치해도 좋다.

네이티브 메소드는 커널에 의해 실행되기 때문에 자바 가상 머신에서 관리하지 않는다.

자바를 실행시키는 가상머신은 기본적으로 모든 문자열을 UTF-16 이라는 유니코드(Unicode)로 처리한다.


앱 build.gradle

cpp가 포함되지 않은 앱 build.gradle 에 비해 자주색으로 표시된 부분이 추가되어 있다.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.link2me.android.ndktest"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}


참고자료

https://programmingfbf7290.tistory.com/entry/NDK2-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-NDK-JNI-%EA%B8%B0%EB%B3%B8-%EC%98%88%EC%A0%9C?category=666988


참고하면 좋은 책 : 안드로이드 C-C++ 프로그래밍

블로그 이미지

Link2Me

,