이지랑드로랑 - 15장. Android Native Development Kit

 

또 다시 찾아온 강좌!

오늘은 NDK 에 대해서 설명하려고 합니다.

또한 앞으로는 안드로이드 포팅보다는 어플리케이션을 만들어가는 것을 강의할까 합니다.

안드로이드 포팅이 필요하다고 생각되는 것이 있으면 그때그때 간단하게 부록으로 추가하도록 하겠습니다.

 

그런 의미에서 이 15장은 이지랑드로랑의 전환점이 될 것 같네요.

하지만 걱정이 있는 것이 어떻게 진행을 해 나갈것이냐라는 것 입니다.

이미 많은 안드로이드 서적이 출간이 되었고, 어플리케이션에 대해서는 정말 잘하시는 분들이 많을 것이기 때문이죠.

 

그래서 전 이지랑드로랑만의 특이한 강좌를 이어가도록 할 것입니다.

어떻게 할 것이냐구요? 최대한 안드로이드를 이용한 개발을 이지보드에서 하는 이유를 찾을 수 있도록 노력해 보겠습니다.

지켜봐 주세요. ^^

 

What?! NDK

NDK, Native Development Kit 를 말합니다.

어원에서 느낌이 오듯이 이 NDK는 C/C++로 프로그램을 개발할 수 있도록 지원하는 개발키트죠.

안드로이드는 기본적으로 개발 언어가 Java로 구성되어 있습니다.

그런 단점을 극복하고자 제공되는 것이 NDK 입니다.

그래서 NDK로 생성된 객체는 JNI(Java Native Interface)라는 것을 이용해서 어플리케이션과 통하게 됩니다.

 

많은 안드로이드 개발 입문자들이 자연스럽게 그러려니 생각하는게 있습니다.

NDK를 이용하면 하드웨어 제어를 자유롭게 할 수 있다!?

정말 그럴까요?

C언어를 이용하면 '무조건' 하드웨어를 제어할 수 있다는 황당한 소문이 여기저기 퍼진것 같습니다.

 

하지만 실상은 그렇지 못합니다. (그렇다고 완전 불가능 한것도 아님)

안드로이드의 NDK를 열어보시면 알겠지만 생각보다 있는 것이 없고 빌드를 스스로 할 수 있는 것도 아닙니다.

우리가 할수 있는 것도 결국은 어플리케이션 권한에서 움직일 수 있는 것이지, 절대로 NDK를 썼다고 안드로이드를 자유자제로 제어할 수 있는 어플리케이션을 만들수 있다고 착각하시면 안됩니다.

안드로이드는 생각보다 권한을 철저히 관리하고 있고 구성있게 잘 개발된 모바일 운영체제 입니다.

 

그래도 저의 이지랑드로랑은 C 컴파일러를 이용해서 재미난 것들을 해볼수 있도록 추구할 겁니다.

그것이 NDK를 사용하는 것은 아니라는 것, 미리 말씀드리겠습니다.

 

[그림15-1. Android NDK]

 

Step1. NDK 다운로드 및 구축

먼저 안드로이드를 다운로드 받아보도록 할까요?

 

NDK 다운로드 : http://developer.android.com/sdk/ndk/index.html

 

[그림15-2. 제공되는 NDK 버전]

 

일단 NDK는 그림14-2에서 보이는 바와 같이 윈도우, 맥, 리눅스 버전이 제공됩니다.

항상 그래왔지만 저는 리눅스를 기반으로 합니다. (I love Linux! ^^)

# wget http://dl.google.com/android/ndk/android-ndk-r3-linux-x86.zip
# unzip android-ndk-r3-linux-x86.zip
# cd android-ndk-r3

자~ 다운로드도 잘 되었고, 압축도 잘 풀었습니다.

이제 빌드를 한번 해 볼까요?

root@svr29:android-ndk-r3# ./build/host-setup.sh
Checking host development environment.
NDK Root   : /staff/hyowon/android/ndk/android-ndk-r3
GNU Make   : make (version 3.81)
Awk        : awk
Platform   : linux-x86
Generate   : out/host/config.mk
Toolchain  : Checking for arm-eabi-4.2.1 prebuilt binaries

Host setup complete. Please read docs/OVERVIEW.TXT if you don't know what to do.

./build/host-setup.sh 를 입력하면 위와 같은 명령이 깔끔하게 나와야 합니다.

그러나 종종 빌드가 되지 않는 경우가 있습니다.

저도 처음 NDK를 빌드할 때 그런 문제가 있었는데 황당하게 해결을 했습니다.

 

{NDK_SRC}/build/host-setup.sh 파일에서 #!/bin/bash 를 자신에게 맞게 고쳐줘야 했었던 기억이 있습니다.

또한 빌드는 반드시 NDK 루트 디렉토리에서 ./build/host-setup.sh 명령어를 이용해서 해야만 합니다.

 

아무튼 이전에 있었던 버그는 NDK-R3에서 수정이 된듯 하네요.

빌드가 깔끔하게 잘 됩니다.

 

Step2. Hello-JNI, 라이브러리 생성하기

항상 그렇지만 첫번째 프로그램은 Hello 입니다. ^^

우리는 Hello-JNI 라는 프로그램을 짜 볼 꺼에요.

NDK에 함께 제공되는 Hello-JNI의 소스 코드를 한번 보도록 합시다.

# vi {NDK_SRC}/apps/hello-jni/project/jni/hello-jni.c
#include < string.h >
#include < jni.h >

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

와우! 소스코드가 정말 간단하네요.

일단 설명하지 않고 그러려니 하고 넘어가고! 곧 바로 make 해보도록 할께요.

# make APP=hello-jni
Android NDK: Building for application 'hello-jni'
Compile thumb : hello-jni <= apps/hello-jni/project/jni/hello-jni.c
SharedLibrary : libhello-jni.so
Install : libhello-jni.so => apps/hello-jni/project/libs/armeabi

위와 같은 화면을 보셨다면 일단은 성공 입니다.

우리에게 필요한 .so 파일이 생성이 되었고 그것이 안드로이드 어플리케이션 소스에서 libs에 포함되었다는 것을 보여주죠.

일단은 만들어진 라이브러리를 사용할 수 있게 된거죠.

 

Step3. Hello-JNI, 어플리케이션에서 JNI 맛보기

이제 만들어진 .so 파일을 어플리케이션에서 사용해 보는 일이 남았습니다.

뭐, 프로젝트를 생성하는 것은 간단합니다만 기억해야할 것이 있습니다.

AVD를 반드시 Google APIs 로 하나 만들어 주신 다음에 에뮬레이터에서 테스트를 하셔야 한다는 것 입니다.

 

[그림15-3. AVD for Google APIs]

 

프로젝트는 이미 존재하는 프로젝트로 해서 열수 있습니다.

이런거 하나만 봐도 참 구글이 안드로이드 개발 입문자들을 위해서 신경을 많이 쓴것 같아요. ^^

 

[그림15-4. AVD for Google APIs]

 

{NDK_SRC}/apps/hello-jni/project 디렉토리를 열면 안드로이드 어플리케이션 샘플 소스를 열 수 있습니다.

Finish 버튼을 누르면 프로젝트가 생성 됩니다.

 

[그림15-5. AVD for Google APIs]

 

자, 어플리케이션에 우리가 만든 .so 파일이 추가된 모습이 보이네요.

HelloJni.java 파일을 한번 볼까요?

... 생략...
package com.example.hellojni;

import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;


public class HelloJni extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        /* Create a TextView and set its content.
         * the text is retrieved by calling a native
         * function.
         */
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }


    /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */

    public native String  stringFromJNI();

    /* This is another native method declaration that is *not*
     * implemented by 'hello-jni'. This is simply to show that
     * you can declare as many native methods in your Java code
     * as you want, their implementation is searched in the
     * currently loaded native libraries only the first time
     * you call them.
     *
     * Trying to call this function will result in a
     * java.lang.UnsatisfiedLinkError exception !
     */
	 
    public native String  unimplementedStringFromJNI();
    /* this is used to load the 'hello-jni' library on application
     * startup. The library has already been unpacked into
     * /data/data/com.example.HelloJni/lib/libhello-jni.so at
     * installation time by the package manager.
     */
	 
    static {
        System.loadLibrary("hello-jni");
    }
}

딱 보니까 System.loadLibrary("hello-jni"); 라는 코드가 일단 우리의 JNI 라이브러리를 부르는 것 같군요.

package com.example.hellojni; 코드가 있는 것으로 보아 패키지가 정상적으로 프로젝트에 추가된 것 같습니다.

그럼 hello-jni 라는 라이브러리가 어디서 이름이 정의되고 있는 것일까요?

# vi {NDK_SRC}/apps/hello-jni/project/jni/Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

네, 그렇습니다. JNI 소스에 Android용 Make 파일에 정의되어 있군요.

자! 그럼 이제 프로그램을 실행시켜 볼까요?

 

[그림15-6. NDK로 빌드된 .so 파일과 JNI로 메시지를 가져온 APK의 모습]

 

잘되는군요.

그렇다면 우리는 이 JNI를 가지고 무엇을 할 수 있을까요?

그것은 여러분 각자의 개발방법론에 달려있습니다. ^^

 

아래는 JNI의 빌드 및 호출 과정을 그림으로 나타낸 것 입니다.

[그림15-7. JNI 빌드 및 호출 흐름도]