2019년 10월 12일 토요일

Android NDK 작성법 -1

C 소스와 연동하기 위해서는 NDK 연동해야 되는데 걍 귀찮네..

일단
고전적인 방법 

1. 액티비티 클래스에서 먼저 static 으로 만들고자 하는 ndk 모듈을 로딩한다.
JniTwo 라는 이름은 C++ 모듈이름으로 Android.mk 에 [ LOCAL_MODULE := jniTwo ] 라고 나중에 똑같이 적으면 된다.

    static {
        System.loadLibrary("jniTwo");
    }

2. 모듈을 로드했으면 액티비티 클래스에서 쓸 함수를 이름만 정의한다.

 private native String getStr(String str);

3. src->main->jni 폴더를 만든다.
4. 그 안에 cpp 파일을 만든다. * 헤더는 나중에 수동을 만들거다.

//
// Created by kimtaeho on 2019-10-12.
//

#include <string.h>

JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_getStr(JNIEnv *env, jobject thiz, jstring str) {
    char buf[255];
    char appendMsg[] = " JNI Hello";

    const char *ch1 = env->GetStringUTFChars(str, 0);
    strcpy(buf, ch1);
    strcat(buf, appendMsg);

    return env->NewStringUTF(buf);

 }

5. 액티비티 클래스에서 함수를 사용해본다.

        TextView ttt = (TextView)findViewById(R.id.ttt);
        ttt.setText(getStr("Kim"));

6. Android Studio Setting 에서 Tools->External Tools 에 새로 생성해서 다음과 같이 적는다.

Program : C:\Program Files\Java\jdk1.8.0_191\bin\javah.exe

Arguments : -classpath "$Classpath$" -v -jni $FileClass$

Working Directory : C:\workspace\MyApplication2\app\src\main\jni
7. 이 상태에서 Build->make Project 를 해본다.
8. 모듈을 로드하는 Activity Class 에서 마우스오른쪽 [External Tools] 에서 6에서 생성한 것을 선택하면 복잡한 h 파일이 생성된다. 해당 파일의 맨 밑에 cpp 에서 만든 함수가 정의되어 있는것을 확인하고 파일이름을 적당히 바꾸고 cpp 에서 include 한다.
9. jni 폴더에 Android.mk 를 만들어 모듈이름고 소스 파일이름을 지정한다.


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := jniTwo
LOCAL_SRC_FILES := Two.cpp
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

10. build.gradle(app) 에 다음과 같이 추가.

import org.apache.tools.ant.taskdefs.condition.Os



// Project Structure에서 설정한 NDK 경로를 읽어 들여 Return합니다.
def getNdkBuildPath() {
    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())

    def command = properties.getProperty('ndk.dir')
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        command += "\\ndk-build.cmd"
    } else {
        command += "/ndk-build"
    }
    return command
}


Android {

 ...



    sourceSets.main {
        // Compile된 Native Library가 위치하는 경로를 설정합니다.
        jniLibs.srcDir 'src/main/libs'
        // 여기에 JNI Source 경로를 설정하면 Android Studio에서 기본적으로 지원하는 Native
        // Library Build가 이루어집니다. 이 경우에 Android.mk와 Application.mk를
        // 자동으로 생성하기 때문에 편리하지만, 세부 설정이 어렵기 때문에 JNI Source의
        // 경로를 지정하지 않습니다.
        jni.srcDirs = []
    }

    ext {
        // 아직은 Task 내에서 Build Type을 구분할 방법이 없기 때문에 이 Property를
        // 이용해 Native Library를 Debugging 가능하도록 Build할 지 결정합니다.
        nativeDebuggable = true
    }

    // NDK의 ndk-build 명령을 이용하여 Native Library를 Build하기 위한 Task를 정의합니다.
    //noinspection GroovyAssignabilityCheck
    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
        if (nativeDebuggable) {
            commandLine getNdkBuildPath(), 'NDK_DEBUG=1', '-C', file('src/main').absolutePath
        } else {
            commandLine getNdkBuildPath(), '-C', file('src/main').absolutePath
        }
    }



    // App의 Java Code를 Compile할 때 buildNative Task를 실행하여 Native Library도 같이
    // Build되도록 설정합니다.
    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn buildNative
    }

    // NDK로 생성된 Native Library와 Object를 삭제하기 위한 Task를 정의합니다.
    //noinspection GroovyAssignabilityCheck
    task cleanNative(type: Exec, description: 'Clean native objs and lib') {
        commandLine getNdkBuildPath(), '-C', file('src/main').absolutePath, 'clean'
    }

    // Gradle의 clean Task를 실행할 떄, cleanNative Task를 실행하도록 설정합니다.
    clean.dependsOn 'cleanNative'



}

11. gradle.properties 에 추가


android.useDeprecatedNdk=true


0 comments:

댓글 쓰기