이 글을 작성했을 때보다 더 나중에 나온

안드로이드 스튜디오 2.2.2로 빌드하는 방법을 새로 작성하였습니다.

http://tibyte.kr/272








개발환경

Windows 10 64bit

Android Studio 1.5.1

jdk 1.8.0_71


target sdk version : 23

min sdk version : 15


기본 설정


- JDK 설치

- JDK 환경변수 (PATH)설정

- 안드로이드 스튜디오 설치

- NDK 다운로드


안드로이드 스튜디오에서 새 프로젝트를 만든다. 

이 포스트에서는 프로젝트 이름을 ndktest로 정한다.


프로젝트 창에서는 Project Files를 선택해야 디렉터리 경로를 보기 쉽다.





File - Project Structure - Android NDK location 에서 다운받은 NDK 경로를 지정한다.

안드로이드 스튜디오를 통해 설치했다면 자동지정된다.






java 어플리케이션 만들기


안드로이드에서 빈 액티비티로 프로젝트를 만들면 TextView하나가 나와있는데,

C/C++ 네이티브 코드에서 보낸 메시지를 여기에 띄워 본다.


프로젝트 디렉터리를 기준으로

app/src/main/res/layout/content_main.xml 

파일에 있는 TextView에게 id를 붙여준다.


    android:id="@+id/textView" 



MainActivity.java 파일에 네이티브 라이브러리를 로드하는 구분과 메서드 선언을 적는다.


  static {

       System.loadLibrary("ndktest");

   }

   public native String getStringFromNative();



onCreate 메서드 내에서는 위에서 선언한 함수를 호출하여 텍스트뷰에 쓴다.


 TextView view = (TextView)findViewById(R.id.textView);

 view.setText(getStringFromNative());



Build - Make Project로 빌드한다.




javah로 JNI용 C++ 헤더파일 제작


이 작업을 하기 전에 먼저 환경변수에 ANDROID_HOME이라는 값을 안드로이드 SDK 경로로 지정해 두면 편하다.



Windows 환경에서 안드로이드 스튜디오를 통해 안드로이드 SDK를 설치했을 때 기본 경로는 C:\Users\유저이름\AppData\Local\Android\sdk 이다.


프로젝트 디렉터리를 기준으로

app/src/main

으로 이동하여 커맨드창을 열고 다음과 같이 javah를 실행한다. (Windows 기준)

도메인 경로와 프로젝트 이름(여기서는 ndktest)는 프로젝트에 맞게 설정.


$ javah -d jni -classpath %ANDROID_HOME%/platforms/android-23/android.jar;%ANDROID_HOME%/extras/android/support/v7/appcompat/libs/android-support-v7-appcompat.jar;%ANDROID_HOME%/extras/android/support/v4/android-support-v4.jar;../../build/intermediates/classes/debug 도메인.경로.ndktest.MainActivity


만약 ANDROID_HOME 경로의 extras/android 디렉터리에 support가 없다면

Tools - Android - SDK Manager - Appearance - System Settings - Android SDK - SDK Tools 로 들어가

Android Support Library 를 설치한다.


성공적으로 실행되었다면

app/src/main/jni 에 헤더파일(.h)이 생성된다.




C++ 네이티브 코드 작성


간단한 C++ 코드를 작성한다.

여기서 주의할 점은 위에서 생성한 헤더파일을 로드해야하고, 함수 이름도

Java_도메인_경로_프로젝트이름_Java클래스명_Java메서드명 형식으로 되어야 한다는 것이다.


JNIEXPORT jstring JNICALL Java_kr_tibyte_ndktest_MainActivity_getStringFromNative(JNIEnv *env, jobject obj)


아래 코드에 있는 도메인 (kr_tibyte)는 프로젝트에 맞게 고친다.


#include "kr_tibyte_ndktest_MainActivity.h"

#include <string>

#include <sstream>


using namespace std;


JNIEXPORT jstring JNICALL Java_kr_tibyte_ndktest_MainActivity_getStringFromNative(JNIEnv *env, jobject obj) {


    stringstream ss;

    for(int i=1; i<=9; i++) {

        for (int j=1; j<=9; j++) {

            int n = i*j;

            ss << " ";

            if(n < 10) ss << "0";

            ss << n;

        }

        ss << endl;

    }


    string str = ss.str();

    return env->NewStringUTF(str.c_str());

}






makefile(*.mk) 설정


app/src/main/jni 안에 Android.mk를 만든다.

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)


LOCAL_MODULE    := ndktest

LOCAL_SRC_FILES := main.cpp

LOCAL_LDLIBS := -llog


include $(BUILD_SHARED_LIBRARY) 


LOCAL_MODULE은 생성될 네이티브 모듈의 이름,

LOCAL_SRC_FILES는 모듈을 만드는 데 사용될 소스파일들의 목록이다.



app/src/main/jni 안에 Application.mk를 만든다.


APP_ABI := armeabi

APP_STL := gnustl_static


여기서는 컴파일을 위한 옵션 플래그들을 지정할 수 있다.

APP_STL을 지정하여 C++에서 STL 요소들을 사용할 수 있다.


APP_ABI는 ABI(Application Binary Interface)를 지정할 수 있는데 armeabi, armeabi-v7a, mips, x86등이 있으니 필요에 따라 설정한다.






gradle 설정

app/build.gradle을 수정한다.


apply plugin: 'com.android.application'


android {

    compileSdkVersion 23

    buildToolsVersion "23.0.2"


    defaultConfig {

        applicationId "kr.tibyte.ndktest"

        minSdkVersion 15

        targetSdkVersion 23

        versionCode 1

        versionName "1.0"

    }

    buildTypes {

        release {

            minifyEnabled false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        }

        debug {

            jniDebuggable true

        }

    }


    sourceSets.main {

        //네이티브 라이브러리 경로

        jniLibs.srcDir 'src/main/libs'

       //JNI소스 경로

        jni.srcDirs = []

    }


   //안드로이드 스튜디오에서 Java 소스를 빌드할 때 buildNative task를 실행시킴

    tasks.withType(JavaCompile) {

        compileTask -> compileTask.dependsOn buildNative

    }


   //네이티브 소스 빌드

    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {

        def ndkDir = android.ndkDirectory

        commandLine "$ndkDir/ndk-build.cmd", 'NDK_DEBUG=1', '-C', file('src/main').absolutePath

    }


    //네이티브 라이브러리 삭제

    task cleanNative(type: Exec, description: 'Clean JNI object files') {

        def ndkDir = android.ndkDirectory

        commandLine "$ndkDir/ndk-build.cmd", '-C', file('src/main').absolutePath, 'clean'

    }

    

    clean.dependsOn 'cleanNative'


}

dependencies {

    compile 'com.android.support:appcompat-v7:23.1.1'

    compile 'com.android.support:design:23.1.1'

}





결과


이제 애플리케이션을 빌드하고 기기에서 실행시키면

C++ 코드에서 처리한 결과가 MainActivity의 TextView로 표시된다.





코드 모음


https://github.com/tibyte/practice/tree/master/android/ndktest





참조


https://stackoverflow.com/questions/21096819/jni-and-gradle-in-android-studio

http://stackoverflow.com/questions/21999829/how-do-i-read-properties-defined-in-local-properties-in-build-gradle

http://stackoverflow.com/questions/21096819/jni-and-gradle-in-android-studio

http://i5on9i.blogspot.kr/2015/02/android-studio-ndk-hello-world.html

https://www.davidlab.net/ko/tech/using-the-android-ndk-with-android-studio-part1/





+ Recent posts