본문 바로가기

Android

[안드로이드]ndk란?

NDK

java만 사용하여 필요한 기능과 성능을 모두 만족시키기는 힘들다. 그래서 C나 C++ 언어로 작성된 프로그램을 java에서 사용할 수 있도록 JDK에서 제공하는 것이 JNI(Java Native Interface) 이다.


그리고 NDK는 Developer문서에서도 볼 수 있듯이

The Android NDK is a toolset that lets you implement parts of your app using native-code languages such as C and C++. For certain types of apps, this can help you reuse code libraries written in those languages.


이것을 가능하게 해주는 툴킷이라고 보면 된다.


NDK를 사용하여 얻을 수 있는 장점

1. 기존에 C로 만들어진 대규모 코드를 JAVA에서 다시 만들 필요없이 재사용이 가능하다.

2. 시스템 디바이스 접근과 JAVA성능을 넘어선 작업이 필요할때 유용하다.

3. 속도 및 성능을 향상시킬 수 있다.


이러한 장점때문에 NDK는 주로 영상처리, 게임, 신호처리, 물리 시뮬레이션 등에 사용된다.



ndk를 사용하기 위해서 리눅스 환경이 필요하다.


1. android SDK Manager에서 NDK를 설치한다.


2. vi /etc/profile 로 ndk경로와 java경로를 설정한다.


3. source /etc/profile 로 적용시킨다.



android프로젝트 생성


main 밑에다가 jni폴더 생성


Android.mk파일과 c파일을 만들고 ndk-build 명령어를 입력하면 libs/x86/libhello-jni.so 파일이 생성된다.





*Android.mk 형식


Android.mk


LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)


LOCAL_MODULE := hello-jni

LOCAL_SRC_FILES := hello-jni.c


include $(BUILD_SHARED_LIBRARY)



hello-jni.c


#include <string.h>

#include <jni.h>


jstring Java_com_example_ta_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)

{


return (*env)->NewStringUTF(env, "Hello From JNI !");

}


*설명
jni.h에 네이티브 코드가 JNI 함수를 호출하는데 필요한 정보를 담고 있음.
java_ 뒤에 com_example_hellojn는 패키지명이고 HelloJni는 클래스명 stringFromJNI는 메서드명이다.
다른것은 그대로 써도 되는데 String은 NewStringUTF로 자바 vm안에 메모리를 만들고 copy해서 리턴해야함

*패키지명을 잘못쓰면  java.lang.UnsatisfiedLinkError: No implementation found 에러가 발생하므로 주의해야한다!!

libhello-jni.so


<?xml version="1.0" encoding="utf-8"?>

<resource>

    <string name="app_name">HelloJni</string>

</resource>



HelloJni.java


package com.example.ta.hellojni;


import android.app.Activity;

import android.widget.TextView;

import android.os.Bundle;


public class HelloJni extends Activity {


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        TextView tv = new TextView(this);


        //call Native function

        tv.setText(stringFromJNI());


        setContentView(tv);


    }


    // java에서 사용할 네이티브 함수의 선언

    public native String stringFromJNI();


    // 네이티브 함수를 사용하기 위하여 동적 라이브러리를 로딩

    static {

        System.loadLibrary("hello-jni");

    }

}


*설명
native라는 키워드는 이 메서드가 java가 아닌 다른 언어로 작성된 것임을 암시.

동적라이브러리는 stringFromJNI메서드가 호출되기 전에 로딩되어야 함을로 static으로 초기화


자동으로 ndk-build 실행하기


build.gradle 을 다음과 같이 작성한다.


apply plugin: 'com.android.model.application'


model {

    android {

        compileSdkVersion = 23

        buildToolsVersion = "23.0.0"


        defaultConfig {

            applicationId = "com.example.ta.ndkadd"

            minSdkVersion.apiLevel = 19

            targetSdkVersion.apiLevel = 23

            versionCode = 1

            versionName = "1.0"

        }


        buildTypes {

            release {

                minifyEnabled = false

                proguardFiles.add(file('proguard-android.txt'))

            }

        }

        ndk {

            moduleName = "ndk-add"

        }



    }


}


dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])

    testCompile 'junit:junit:4.12'

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

}


그리고 run을 시켜주면 

자동으로 .so파일이 생성된다.


C로 작성된 소스를 안드로이드 스튜디오의 logcat에서 디버깅하려면


1. gradle에 작성한 ndk부분을 다음과 같이 수정한다.


ndk {

            moduleName = "ndk-call"

            toolchain = "clang"

            stl = 'gnustl_static'

            cppFlags.addAll(['-std=c++11', '-Wall', '-D__STDC_INT64__'])

            ldLibs.addAll(['android', 'log'])

        }


2. c소스파일에 

#include <android/log.h> 

를 추가해준다.


3. c소스파일에 디버그명령어를 입력하면 logcat에서 볼 수 있다.

__android_log_print (ANDROID_LOG_INFO, "Ndk-call", "Hello~~~");