2. 안드로이드 NDK
- 안드로이드 SDK와 함께 이용되는 개발키트
=> 안드로이드 NDK를 사용하면 애플리케이션의 일부 또는 전부를
c/c++로 만들수 있다.
=> NDK로 만들어진 실행파일은 바이너리코드이다.(기계어)
- NDK 포함된 것들
=> 컴파일러, 링커,ToolChain,헤더파일,라이브러리,문서, 예제소스
- SDK가 기본으로 사용되며, NDK는 보조적인 역할을 하기 위해 사용
- NDK에서 할수 있는 것들
=> OpenGL/ES , OpenSL/ES(사운드), 센서입력,터치패널
=> NativeActivity , 리눅스시스템콜
=> Assets폴더로부터 데이터입력(파일을 추가할때마다 빌드필요없다)
3. 안드로이드 내부구조(계층)
- 리눅스 커널 => 하드웨어 추상계층(HAL) => 라이브러리,안드로이드런타임
=> 애플리케이션 프레임워크 => 애플리케이션
- 안드로이드 런타임 : (달빅VM)
- 애플리케이션 프레임워크 : 버튼,텍스트박스 같은 애플리케이션을
동작시키는데 필요한 라이브러리가 포함
- 애플리케이션 실행환경
=> 자바소스 => VM바이트코드 =>VM =>라이브러리 => 커널
- NDK 실행환경
=> 네이티브코드 => 라이브러리 => 커널
자바로 만들어지 부분중 NDK코드는 달빅VM을 거치지 않고 바로
라이브러리를 호출한다.
4. 안드로이드 내부구조(계층)
- SDK,NDK같이 개발한 소스는 VM바이트코드+네이티브코드 포함한
하나의 실행파일에 공존하게 된다.
- NDK경우 c/c++로 만든코드는 컴파일러와 링커를 거쳐 .so 파일로 생성
=> 공유라이브러이 이므로 단독으로 동작할수 없다.
- 자바코드 (.dex파일)로 부터 호출하여 .so 모듈을 이용
- 자바코드을 빌드할때 모듈(.so)을 이용하여 실행파일(.apk)생성
- NDK단점
1. 디버깅이 어렵다
2. 디바이스마다 실행파일 만들어야한다.(하드웨어 의존성)
- NDK장점
1. 빠른속도의 데이터 처리(이미지처리, 게임등)
2. c/c++ 라이브러리 사용
5. 안드로이드 NDK 설치 for OS X
- NDK 설치는 압축파일 형태로 배포되어 압축을 풀기만 하면 완료.
- 맥OS X 기준으로 설명
1. http://developer.android.com/sdk/ndk/index.html (NDK다운)
2. 임시폴터에 압축풀기
$ tar xvf android-ndk-r5c-darwin-x86.tar.bz2
$ sudo mv android-ndk-r5c /Developer/SDKs/
3. android-ndk-r5폴더에 포함된 명령어 실행하도록 패스 추가
export $PATH=/Developer/SDKs/android-ndk-r5c:$PATH
6. 안드로이드 NDK 설치 for Window
- Window 기준으로 설명
1. www.cygwin.com에서 시그윈설치(GNU make필요하기 때문)
=> 리눅스에서 자주 사용되는 명령어를 윈도우에서 가능하게
=> Cygwin 설치 시에 Select Packages 단계에서 'make'를 체크 후 설치
2. 큰 카테고리에 +를 눌러서 활성화 시키면 아래 항목들 활성화
Devel : gcc-core, gcc-g++, make, swing
Editor : vim
* Devel통째로 받아서 설치 추천~!!
3.Windows 환경변수에서 PATH설정
C:cygwinbin;C:cygwinusrinclude 추가
7. 안드로이드 NDK 설치 for Window
- Window 기준으로 설명
4. http://developer.android.com/sdk/ndk 에서 ndk 를 받는다
5. NDK 의 압축을 푼다. (cygwin/home/계정/하위폴더에 설치)
=> C:cygwinhomeAdministratorandroid-ndk-r8c
6. cygwinhome(사용자계정)Administrator.bashrc 파일수정
export ANDROID_NDK_ROOT= homeAdministratorandroid-ndk-r8c
8. 안드로이드 NDK 설치 for Window
6. 환경변수의 시스템변수에서 새로만들기
ANDROID_SDK 변수이름, 값은 설치한디렉토리
ANDROID_NDK 변수이름, 값은 설치한 디렉토리
7. 환경변수의 시스템변수-> PATH 값 설정
C:Program Files (x86)Androidandroid-sdktools;
C:Program Files (x86)Androidandroid-sdkplatform-tools;
C:cygwinhomeAdministratorandroid-ndk-r8c;
9. 안드로이드 JNI
- 자바에서 c/c++의 함수를 사용할수 있게 해주는 인터페이스
- JNI 규약
1. 메모리 관리
=> 자바는 달빅VM이 메모리 관리하지만 c/c++은 개발자가
메모리 관리를 책임지고 할당,해제 해야한다.
2. 타입선언
=>자바의 자료형을 c/c++에서 사용할때 자바자료형 앞에j를 붙인다.
(예: byte=>jbyte , int=>jint , float=>jfloat , object=>jobject)
=>자바의 배열형은 c/c++에서 Array를 붙여서 선언
(예: byte[] => jbyteArray)
3. 시그니처: 메소드를 만들때 사용하는 독특한 형태
=>원시타입은 알파벳 한글자로 표현(byte=>B , long=>J)
=>클래스앞에는 L,끝에는 세미콜론을 붙인다.
=>클래스명은 패키지명을 포함한 문자열로 지정
(예: Ljava/lang/String; => 자바의 String타입일경우)
=>배열은 선투에 '['을 붙여서 지정
(예: int[] => [I; , String[]=> [Ljava/lang/String;
11. 안드로이드 JNI
5. 메소드 정의 규칙
* 자바측
- 호출하는 함수를 선언할때 native 추가
- loadLibrary()를 이용해 호출할 라이브러리를 지정
public native int addVals(int a,int b); // native 붙인다
static{ System.loadLibrary("calcvals"); //libcalcvals.so 로드
}
* c/c++ 측
- 함수명은 java로 시작, 패키지명+클래스명+메서드명 연결하여 기술
- 인수중 처음2개의 인수는 JNIEnv,jObject타입으로 한다.
#include <jni.h>
jint java_com_solab_calcval_MainActivity_addVals(JNIEnv* env,jObject thiz , jint a, jint b)
{
return a+b;
}
=>co.solab.clacval 패키지에 있는 MainActivity 클래스의 addVals메서드 호출
12. 안드로이드 JNI
6. 리플렉션
=> c/c++에서 자바의 클래스에 접근할수 있는 기능(자바함수, 자바 변수 호출)
예: 문자열을 넣으면 숫자값으로 변화하는 자바클래스의 parseInt 함수 호출
jclass jklass= (*env)->FindClass(env,"java/lang/Integer");
- FindClass함수를 이용하여 java.lang.Integer클래스를 가져온다.
jmethodID jmethod = (*env)->GetStaticMethodID(env, jklass,"parseInt","(Ljava/lang/String);I");
- jmethod 에 "parseInt"와 인수, 반환값을 전달하고 해당 메서드의 ID를 구한다.
if(jmethod == NULL)return;
jint value =(*env)->CallStaticIntMethod(env, jklass,jmethod,strInt);
- jklass 클래스와 jmethod를 실행
예: intValue라는 int타입의 멤버변수를 가져오는 코드
jfieldID jfieldid = (*env)->GetFieldID(env, instance,"intValue","I");
- 세번째인수 : 가져올 변수명, 네번째인수: 가져올 변수타입
if(jfieldid = NULL)return;
jint val = (*env)->GetIntField(env,instance,jfieldid);
- GetXXXField 멤버변수를 int 타입으로 가져온다.
13. 안드로이드 JNI
7. C/C++에서 자바의 배열 사용법
- 자바의 배열을 가져올려면 GetXXXArrayElements(XXX)
- releaseArrayElements 배열메모리 영역 해제
8. C/C++에서 자바의 예외 발생
- ExceptionCheck 함수
- ExceptionOccurred 함수
- ExceptionClear 함수
9. 문자열
- UTF-8인코딩된 문자열
jstring jstr =(*env)->NewStringUTF(env, "펭귄");
jsize jlen =(*env)->GetStringLength(env,jstr);
const char* bytes =(*env)->GetStringUTFChars(env,jstr,NULL);
(*env)->ReleaseStringUTFChars(env,jstr,bytes);
14. 안드로이드 JNI
10. 대규모 메모리 처리
- 자바에서 C/C++로 이미지 데이터 보내기
1. 자바의 배열을 이용하기
: 자바에서 설정한 배열을 C/C++용으로 변환해서 저장
2. java.nio.ByteBuffer클래스 이용
: 변환과정 필요없어 처리시간 단축
NewDirectByteBuffer(JNIEnv * env,viud* address, jlong capacity)
-> 메모리 주소와 크기를 설정하고 새로운 인스턴스 생성
GetDirectBufferAddress(JNIEnv *env, jobject buf)
-> 인스턴스를 전달하고 시작주소를 반환한다.
GetDirectBufferCapacity(JNIEnv *env, jobject buf)
-> 인스턴스를 전달하고 크기를 반환한다.
11. 리틀 엔디언, 빅 엔디언
데이터를 메모리에 저장할때 상위비트부터 저장 : 빅에디언
데이터를 메모리에 저장할때 하위비트부터 저장 : 리틀에디언
=>달빅VM에서는 리틀에디언 사용
15. 안드로이드 JNI
12. Android.mk 파일
- 안드로이드 NDK를 이용하여 빌드할려면 Android.mk파일이 필요
- c/c++에서 makefile을 만들어서 빌드하지만 안드로이드는 Android.mk 파일만
듬
- 모듈을 만드는데 필요한 소스코드 및 링크할 라이브러리 지정
- 문법은 GNU make에 따라 작성
예: clacvlas.c에서 모듈인 libcalcvalc.so 파일을 생성
LOCAL_PATH :=$(call my-dir)
include $(CLEAR_VARS)
-> 이전의 모든 설정을 정리
LOCAL_MODULE := calcvals
-> 생성할 모듈 지정 ,libcalcvalc.so 라는 공유라이브러리가 생성
LOCAL_SRC_FILES := calcvals.c
-> 생성된 모듈을 빌드하는데 필요한 소스파일을 지정
include $(BUILD_SHARED_LIBRARY)
-> 이 라이브러리는 공유라이브러리로 생성
16. 안드로이드 JNI
- 자바에서 C 함수 호출하기
* int타입만 사용해 사칙연산 메소드 만들기 실습 *
1. 프로젝트 만들기
- cygwin/home/user계정/ 폴더에 프로젝트를 생성한다.
- 이클립스에서 프로젝트 생성후 소스코드와 Android.mk 파일위한 jni폴더 추가
2. jni폴터 밑에 Android.mk 파일 생성
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := calcvals.c
LOCAL_MODULE := calcvals
include $(BUILD_SHARED_LIBRARY)
=> 이 파일에 의해 calcvals.c가 빌드되고 libcalcvals.so파일이 만들어진다.
17. 안드로이드 JNI
3. MainActivity.java 변경
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StringBuffer buf = new StringBuffer();
TextView tv = new TextView(this);
int a=10;
int b=2;
int val = addVals(a,b);
buf.append("a+b="+String.valueOf(val)+"n");
val = subVals(a,b);
buf.append("a-b="+String.valueOf(val)+"n");
val = mulVals(a,b);
buf.append("a*b="+String.valueOf(val)+"n");
val = divVals(a,b);
buf.append("a/b="+String.valueOf(val)+"n");
tv.setText(buf.toString());
setContentView(tv);
}
public native int addVals(int a, int b);
public native int subVals(int a, int b);
public native int mulVals(int a, int b);
public native int divVals(int a, int b);
static{
System.loadLibrary("calcvals");
}
18. 안드로이드 JNI
4. calcvals.h 파일 생성
#include <jni.h>
#ifndef _addvals_h
#define _addvals_h
#ifdef __cplusplus
extern "C"
{
#endif
jint Java_com_solab_jnicalc_MainActivity_addVals(JNIEnv* env,jobject thiz,jint a, jint
jint Java_com_solab_jnicalc_MainActivity_subVals(JNIEnv* env,jobject thiz,jint a, jint
jint Java_com_solab_jnicalc_MainActivity_mulVals(JNIEnv* env,jobject thiz,jint a, jint
jint Java_com_solab_jnicalc_MainActivity_divVals(JNIEnv* env,jobject thiz,jint a, jint
#ifdef __cplusplus
}
#endif
#endif
19. 안드로이드 JNI
5. calcvals.c 파일 생성
#include "calcvals.h"
jint Java_com_solab_jnicalc_MainActivity_addVals(JNIEnv* env,jobject thiz,jint a, jint b)
{
return a+b;
}
jint Java_com_solab_jnicalc_MainActivity_subVals(JNIEnv* env,jobject thiz,jint a, jint b)
{
return a-b;
}
jint Java_com_solab_jnicalc_MainActivity_mulVals(JNIEnv* env,jobject thiz,jint a, jint b)
{
return a*b;
}
jint Java_com_solab_jnicalc_MainActivity_divVals(JNIEnv* env,jobject thiz,jint a, jint b)
{
return a/b;
}
20. 안드로이드 JNI
6. 빌드
- 먼저, 프로젝트 폴더로 이동하여 ndk-build명령을 실행한다.
- 빌드에 성공하면 libs/armeabi 폴더 아래에 libcalcvals.so생성됨
- 이클립스에서 project -> clean 후에 프로그램 실행