본문 바로가기

카테고리 없음

NDK를 이용한 Android 예제

서너달 만에 다시 안드로이드를 할려기 기억이 가물 가물하다.
예전에 하던거 쉽게 까먹는다 그래서 예전에 보고 하던 실습 예제 스크립해 둔다. 까먹어도 쉽게 찾기 위해서...

http://viiiin.tistory.com/12

1. 개발환경

Host OS
  - Windows 7 Ultimate x86

Cygwin
  - version 2.712
  - 
http://www.cygwin.com

Eclipse
  - Eclipse IDE for Java Developers : Galileo Packages(ver 3.5)
  - http://www.eclipse.org/downloads

JDK
  - Java SE Development Kit 6u21
  - http://www.oracle.com/technetwork/java/javase/downloads/index.html

Android SDK & NDK
  - Android SDK Package : android-sdk_r06-windows.zip
  - Android NDK Package : android-ndk_r4b-windows.zip
  - Android SDK and AVD Manager(Packages and Archives) : http://dl-ssl.google.com/android/eclipse
  - http://developer.android.com/sdk/index.html


2. Cygwin 설치 및 환경설정

Cygwin을 사용하는 이유는 기본적으로 Android 개발 환경이 Linux 기반으로 되어 있기 때문이고, ndk-build 명령으로 빌드하기 위함입니다. 개인적으로 MinGW를 사용하시는 분은 MinGW에서 하셔도 좋습니다.

Cygwin 설치 시에 Select Packages 단계에서 'make'를 체크 후 설치해 줍니다.


저는 이미 설치되어 있기 때문에 'Keep'이라고 나오는 것입니다. 'Skip'부분을 클릭하면 'Install'로 바꿀 수 있습니다. 같은 방법으로 Search 기능을 이용하여 'vim'을 검색 후, Editors 카테고리의 vim을 'Install'로 바꾼 후 설치해 줍니다. 혹시 나중에 추가로 필요한 패키지가 생길 경우 다운받은 Cygwin 폴더의 setup.exe를 실행시켜 패키지를 추가할 수 있습니다.

다음으로 Cygwin을 실행하여 ndk-build 명령을 어디서든 실행할 수 있게 하기 위해 .bash_profile 파일의 PATH 환경변수에 경로를 추가시켜 줍니다.


/cygdrive 는 윈도우의 '내컴퓨터'와 같은 경로입니다. 저의 경우에는 다운받은 NDK 폴더가 아래의 경로에 있기 때문에 그림과 같이 PATH설정을 해 준 것입니다. 본인의 경로에 맞게 수정해주면 됩니다.

D:\UTIL\etc\Android_NDK_r4b\android-ndk-r4b

그럼 이제 cygwin은 모두 준비가 끝난 상태입니다.


3. Eclipse Android Project 생성

이제 Eclipse를 이용하여 안드로이드 프로젝트를 만들어 보도록 하겠습니다.

먼저, [File]->[New]->[Project]를 선택하면 다음과 같은 창이 나옵니다.



Android Project를 선택한 후, Next를 클릭합니다.

아래 그림과 같이 빈 곳을 채워준 다음 Finish를 클릭합니다. 이 때, 저는 "Use default location"을 체크해제한 후, cygwin의 홈디렉토리에 프로젝트 폴더를 만든 후 이곳의 경로를 지정해 주었습니다.



다음 그림과 같이 프로그래밍할 준비를 마쳤습니다.




4. 예제 프로그래밍

그럼 이제 본격적으로 안드로이드 App 예제를 실행하기 위한 프로그래밍을 해 보도록 하겠습니다.

① Activity Layout 구성
위치 : [HelloAndroid]-[res]-[layout]-[main.xml]
텍스트뷰와 버튼을 각각 하나씩 추가합니다.
01.<?xml version="1.0" encoding="utf-8"?>
02.<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03.android:orientation="vertical"
04.android:layout_width="fill_parent"
05.android:layout_height="fill_parent"
06.>
07.<Button
08.android:id="@+id/callbtn"
09.android:layout_width="wrap_content"
10.android:layout_height="wrap_content"
11.android:text="Call JNI"
12./>
13.<TextView 
14.android:id="@+id/rettxt"
15.android:layout_width="fill_parent"
16.android:layout_height="wrap_content"
17.android:textSize="15pt"
18./>
19.</LinearLayout>


② Activity 구현
위치 : [HelloAndroid]-[src]-[ssu.os.android]-[NdkTestActivity.java]
버튼 클릭으로 jni를 호출하는 소스를 onCreate에 구현하였습니다.
혹시 에러가 나거든 [Ctrl+Shift+O]를 이용해 자동으로 import를 수행해 줍니다.
01.package ssu.os.android;
02. 
03.import android.app.Activity;
04.import android.os.Bundle;
05.import android.util.Log;
06.import android.view.View;
07.import android.view.View.OnClickListener;
08.import android.widget.Button;
09.import android.widget.TextView;
10. 
11.public class NdkTestActivity extends Activity {
12./** Called when the activity is first created. */
13.private Button bv;
14.private TextView tv;
15. 
16.@Override
17.public void onCreate(Bundle savedInstanceState) {
18.super.onCreate(savedInstanceState);
19.setContentView(R.layout.main);
20. 
21.bv = (Button)findViewById(R.id.callbtn);
22.tv = (TextView)findViewById(R.id.rettxt);
23. 
24.bv.setOnClickListener(new OnClickListener() {
25.public void onClick(View v) {
26.NativeCall nativeCall = new NativeCall();
27.int ret = nativeCall.add(10, 20);
28.String retStr = nativeCall.stringFromJNI();
29.Log.i("TAG", retStr + ret);
30.tv.setText(retStr);
31.}
32.});
33.}
34.}


③ Class 만들기
위치 : [HelloAndroid]-[src]-[ssu.os.android]-[NativeCall.java]
위의 위치에 NativeCall.java 파일을 새로 생성합니다.
native 함수가 들어있는 NativeCall 클래스를 생성합니다. native 키워드는 javah가 헤더파일을 생성할 때 참조하게 되는 키워드 입니다. 구현부는 없습니다. static 부분은 class의 객체가 생성될 때 (new) 되어질 때 호출됩니다. "my_lib"는 ndk-build로 생성된 라이브러리 이름입니다. 파일명은 libmy_lib.so 입니다.
01.package ssu.os.android;
02. 
03.public class NativeCall {
04.static {
05.System.loadLibrary("my_lib");
06.}
07.public native String stringFromJNI();
08.public native int add(int a, int b);
09.}


④ javah 실행
~/bin 디렉토리에서 javah를 실행합니다. javah는 내부적으로 ./ssu/os/android/NativeCall.class 파일을 참고하여 헤더파일을 생성하게 됩니다. 헤더 파일명은 패키지명과 클래스명을 참고해서 생성됩니다. -o 옵션을 사용하면 파일명을 임의로 지정할 수 있습니다. 프로젝트 폴더에 /jni 폴더를 생성하고, 만들어진 헤더파일을 옮겨줍니다.


- 생성 된 헤더파일(ssu_os_android_NativeCall.h) 내용
native 키워드로 되어있던 함수들이 jni 형태 함수 프로토타입을 확인할 수 있습니다.
복잡해 보이지만 C문법입니다.
01./* DO NOT EDIT THIS FILE - it is machine generated */
02.#include <jni.h>
03./* Header for class ssu_os_android_NativeCall */
04. 
05.#ifndef _Included_ssu_os_android_NativeCall
06.#define _Included_ssu_os_android_NativeCall
07.#ifdef __cplusplus
08.extern "C" {
09.#endif
10./*
11.* Class:     ssu_os_android_NativeCall
12.* Method:    stringFromJNI
13.* Signature: ()Ljava/lang/String;
14.*/
15.JNIEXPORT jstring JNICALL Java_ssu_os_android_NativeCall_stringFromJNI
16.(JNIEnv *, jobject);
17. 
18./*
19.* Class:     ssu_os_android_NativeCall
20.* Method:    add
21.* Signature: (II)I
22.*/
23.JNIEXPORT jint JNICALL Java_ssu_os_android_NativeCall_add
24.(JNIEnv *, jobject, jint, jint);
25. 
26.#ifdef __cplusplus
27.}
28.#endif
29.#endif


⑤ 함수 구현
위치 : [HelloAndroid]-[jni]-[my_lib.c]
/jni 폴더에 my_lib.c 파일을 만들어서 다음과 같이 함수를 구현합니다. 두 개의 함수가 있는데 단순히 string을 리턴해 주는 함수와 두 정수를 입력받아 더한 후 리턴하는 함수입니다.
01.#include "ssu_os_android_NativeCall.h"
02. 
03.JNIEXPORT jstring JNICALL Java_ssu_os_android_NativeCall_stringFromJNI(JNIEnv *env, jobject obj)
04.{
05.return (*env)->NewStringUTF(env, "Hello JNI!");
06.}
07. 
08.JNIEXPORT jint JNICALL Java_ssu_os_android_NativeCall_add(JNIEnv *env, jobject obj, jint a, jint b)
09.{
10.return a + b;
11.}


⑥ Android.mk 파일 작성
위치 : [HelloAndroid]-[jni]-[Android.mk]
다운받은 Android NDK 폴더의 /sample/hello-jni/jni 의 Android.mk 파일을 현재 프로젝트의 /jni 폴더에 복사한 후, 다음과 같이 수정하여 사용합니다. 수정 된 내용은 19, 20 라인의 LOCAL_MODULE 과 LOCAL_SRC_FILES 입니다.
01.# Copyright (C) 2009 The Android Open Source Project
02.#
03.# Licensed under the Apache License, Version 2.0 (the "License");
04.# you may not use this file except in compliance with the License.
05.# You may obtain a copy of the License at
06.#
08.#
09.# Unless required by applicable law or agreed to in writing, software
10.# distributed under the License is distributed on an "AS IS" BASIS,
11.# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12.# See the License for the specific language governing permissions and
13.# limitations under the License.
14.#
15.LOCAL_PATH := $(call my-dir)
16. 
17.include $(CLEAR_VARS)
18. 
19.LOCAL_MODULE    := my_lib
20.LOCAL_SRC_FILES := my_lib.c
21. 
22.include $(BUILD_SHARED_LIBRARY)


⑦ NDK Build
프로젝트 루트 혹은 /jni 디렉토리에서 "ndk-build" 명령을 실행합니다.


빌드가 끝나면 프로젝트 폴더 밑에 /libs 폴더가 생성되고, 라이브러리인 .so 파일이 생성 된 것을 확인할 수 있습니다.




⑧ 에뮬레이터 실행
위의 과정이 정상적으로 진행되었으면 Eclipse에서 에뮬레이터를 실행시켜 줍니다. 안드로이드 부팅 과정이 끝나면 우리가 작성한 App을 볼 수 있을 것입니다.


Call JNI" 버튼을 누르게 되면 jni가 호출되어 스트링을 가져와 화면(TextView)에 뿌려주게 됩니다.



<참고 사이트>
[1] http://shchoi82.springnote.com/pages/6150375