2013년 3월 12일 화요일

[Android] Android NDK를 이용한 예제 작성

* 지난 번에 Android NDK의 빌드 환경을 설정했다. 그 때에는 빌드 환경을 설정하고, Android에서 제공하는 예제를 실행해 보는 것으로 끝났었다. 이제 Android NDK로 빌드하는 예제를 만들어 보고자 한다.

* Android NDK 예제를 찾다 보니, 잘 설명된 blog가 있어서, 그대로 따라하면서 Project를 생성해 본다.

1. 개발 환경

 * 개발 환경에 대해서는 지난 번 글([Android] Android NDK 빌드 환경 꾸미기)에서 했던 내용이다.

1-1. cygwin

Windows의 경우, cygwin 설치가 되어 있어야 한다. command창에서 javah나 ndk-build 명령을 실행한다.

2. Eclipse : Android Project 생성

 * Eclipse workspace에 새로운 프로젝트를 생성하자[File -> New -> Android Application Project].

 * Next를 클릭하여 다음으로 진행한다.
 * 프로젝트 정보
   - Application Name : NDKTest
   - Package Name : com.shnoh.ndktest
   - Activity Name : NDKTestMainActivity

package com.shnoh.ndktest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class NDKTestMainActivity extends Activity {

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

2-1. Layout 구성

 * 단말 화면에 보여질 Layout을 구성한다.
   - 파일 위치 : NDKTest\res\layout\activity_ndktest_main.xml
 * Button 하나와 TextView 하나를 추가한다.



2-2. Activity 구현

 * Button과 TextView의 View ID를 가져온다.
 * Button을 눌러 JNI 함수를 호출하는 부분을 추가한다.
 * Native 함수를 호출할 class 이름을 NativeCall 이라고 만든다.

package com.shnoh.ndktest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class NDKTestMainActivity extends Activity {

 private Button mBtn;
 private TextView mTextView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ndktest_main);
        
        mBtn = (Button) findViewById(R.id.callBtn);
        mTextView = (TextView) findViewById(R.id.textVal);
        
        mBtn.setOnClickListener(new OnClickListener() {
   
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                NativeCall nativeCall = new NativeCall();
                int ret = nativeCall.add(10,20);
                String retStr = nativeCall.stringFromJNI();
                mTextView.setText(retStr);
            }
        });
    }   
}

2-3. JNI를 위한 class 생성

 * 앞 단계에서 이름 지은 NativeCall class를 생성한다.
   - 파일 위치 : NDKTest\src\com.shnoh.ndktest\NativeCall.java
 * 소스 내용
   - native 키워드 : javah 가 헤더(header) 파일을 생성할 때 참조하는 키워드임.
   - 구현부는 없음.
   - static 부분 : class 객체가 생성될 때(new 될 때) 호출됨.
   - System.loadLibrary("my_lib") : ndk-build로 생성된 lib 이름("my_lib").
   - Library 파일 명 : libmy_lib.so

package com.shnoh.ndktest;

public class NativeCall {

    static {
     System.loadLibrary("my_lib");
    }
    
    public native int add(int i, int j);

    public native String stringFromJNI();
 
}

2-4. javah 실행 : Native 소스를 위한 header 파일 생성

 * Eclipse의 NDKTest 프로젝트를 빌드를 하여, NativeCall.class 파일을 만든다.
 * Native 소스를 위한 header 파일 생성하기 위해 command창을 열어 javah 를 실행한다.
 * javah 의 실행 위치 및 command
   - 프로젝트 root 폴더에서 실행 :
     i. 명령어 : NDKTest>javah -classpath bin/classes com.shnoh.ndktest.NativeCall


     i. 결과 화면

   - 프로젝트 bin 폴더에서 실행 :
     i. NDKTest\bin>javah -classpath classes com.shnoh.ndktest.NativeCall


     i. 결과 화면

   - 한번만 해도 될 것을 테스트성으로 각기 다른 폴더에서 javah를 실행함.

 * 이제 프로젝트 루트 폴더에 /jni 폴더를 만들어, 위에서 만든 헤더 파일을 이동한다.


   - header 파일 내용

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_shnoh_ndktest_NativeCall */

#ifndef _Included_com_shnoh_ndktest_NativeCall
#define _Included_com_shnoh_ndktest_NativeCall
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_shnoh_ndktest_NativeCall
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_shnoh_ndktest_NativeCall_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     com_shnoh_ndktest_NativeCall
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_shnoh_ndktest_NativeCall_stringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

2-5. Native 소스 구현

 * jni 폴더에 my_lib.c 파일을 만들어, 위에서 선언한 함수들을 구현한다. 2개의 함수이다.
   - String을 return 해주는 함수
   - 2개의 정수를 입력 받아 합을 return 해주는 함수
 * my_lib.c 파일 내용

#include "com_shnoh_ndktest_NativeCall.h"

JNIEXPORT jstring JNICALL Java_com_shnoh_ndktest_NativeCall_stringFromJNI(JNIEnv *env, jobject obj)
{
    return (*env)->NewStringUTF(env, "Hello JNI!!!!!");
}

JNIEXPORT jint JNICALL Java_com_shnoh_ndktest_NativeCall_add(JNIEnv *env, jobject obj, jint i, jint j)
{
    return i + j;
}

2-6. Android.mk 파일 작성

 * 지난 번에 설치한 Android NDK의 Sample 폴더에 보면, hello-jni 폴더가 있다. 이 프로젝트의 Android.mk 파일을 가져와서 수정을 하자.
 * 수정된 부분은 LOCAL_MODULE 과 LOCAL_SRC_FILES 항목이다.
   - Android.mk 파일 내용

# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := my_lib
LOCAL_SRC_FILES := my_lib.c

include $(BUILD_SHARED_LIBRARY)

3. ndk-build : command 창 

 * 프로젝트의 root 폴더 또는 /jni 폴더에서 "ndk-build" 라는 명령을 실행한다.
   - 프로젝트 root 폴더에서 실행
     i. 명령어 : NDKTest>ndk-build


     i. 결과 화면

 * 빌드가 수행되고 나면, libs/armeabi 폴더에 "libmy_lib.so" 파일이 생성된다.

4. 실행 및 설치 : apk 빌드 및 설치

 * Eclipse 프로젝트에서 빌드 후 apk를 설치한다.

5. 실행 화면

 * apk를 실행하면 아래와 같은 화면을 볼 수 있다.



 * "Call JNI" Button을 누르면, "Hello JNI!!!!!" 라는 String이 보인다.


 * 2개의 정수를 더한 값을 return하는 것은 연결되어 있지 않다.

6. 샘플 소스

 * 위에서 사용한 샘플 소스이다.
   - https://snoh@bitbucket.org/snoh/androidndk
   - Mercurial command : hg clone https://snoh@bitbucket.org/snoh/androidndk

7. 출처 및 참고 사이트


7-1. 안드로이드 공식 사이트

 * Getting Started with the NDK
   -  http://developer.android.com/tools/sdk/ndk/index.html#GetStarted

7-2. 개인 블로그

 * Memories collected..."추억수집" : NDK를 이용한 안드로이드 App 기본예제
   - http://viiiin.tistory.com/12
 * 박상현님의 블로그 : javah 사용법
   - http://blog.naver.com/PostList.nhn?blogId=multisunchon&categoryNo=26&from=postList




댓글 없음:

댓글 쓰기