* 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. 실행 화면
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
- http://blog.naver.com/PostList.nhn?blogId=multisunchon&categoryNo=26&from=postList