2011년 11월 16일 수요일

Android Native 공유 라이브러리 연동

[개발환경]
1. Cygwin 설치
  가. http://www.cygwin.com 에서 다운받는다.
  나. Select package 화면에서 gcc-core, g++, make 패키지를 검색해서 설치한다.


2. NDK 설치
  가. http://developer.android.com/sdk/ndk/index.html 에서 다운받아 압축을 푼다.
  나. 시스템 변수 path영역에 압축푼 경로를 추가한다.
       ex) D:\AndroidNDK\android-ndk-r6b


3. 테스트
  가. cygwin을 실행해서 >ndk-build 를 실행하여 관련 메시지가 나타나는지 확인한다.




[개발 테스트]
1. Android  프로젝트를 생성한다.
  가. 프로젝트 생성 : NDKExam
// =start=======================================================

package org.example.ndk;


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


public class NDKExam extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        TextView tv = new TextView(this);
        int x = 1000;
        int y = 42;
        
        System.loadLibrary("ndk-exam");
        
        int z = add(x, y);
        
        tv.setText("The sum of " + x + " and " + y + " is " + z);
        setContentView(tv);
    }
    
    public native int add(int x, int y);
}

// = end =======================================================




2. 헤더 생성은 링크 클릭 => JNI 네이티브 함수 헤더 생성하기




3. JNI 네이티브 함수 구현하기
  가. eclipse 패키지에서 "jni" 폴더 생성한다.
  나. second.c 파일을 생성한다.
// =start=======================================================

#include "first.h"
#include <jni.h>


jint Java_org_example_ndk_NDKExam_add(JNIEnv* env,
 jobject this,  jint x,
 jint y)
{
return first(x, y);
}

// = end =======================================================




  다. first.c 파일을 생성한다.
// =start=======================================================

// first.c
#include "first.h"


int first(int x, int y)
{
return x + y;
}

// = end =======================================================


  라. first.h 파일을 생성한다.
// =start=======================================================

// first.h
#ifndef FIRST_H
#define FIRST_H


extern int first(int x, int y);


#endif /* FIRST_H */

// = end =======================================================


  마. jni 폴더에 Android.mk 파일을 생성한다.
// =start=======================================================

# 소스 파일 위치
LOCAL_PATH:= $(call my-dir)


# Make 관련 환경 변수를 초기화
include $(CLEAR_VARS)


# 라이브러리를 빌드하기 위한 정보 생성(라이브러리 이름, 소스 코드등)
LOCAL_MODULE := ndk-exam
LOCAL_SRC_FILES := first.c second.c


# 공유 라이브러리 생성
include $(BUILD_SHARED_LIBRARY)

// = end =======================================================


※ Android.mk 작성 문법 관련해서는 <NDK_HOME>\docs 파일 참조




  바. <PROJECT-ROOT>ndk-build 실행


  사. eclipse에서 android run하여 에뮬이나 디바이스에서 결과 확인.

JNI 네이티브 함수 헤더 생성하기

오랜 인고의 이틀간 삽질 끝에 드디어 파일이 생성 되었다.. ㅠㅠ;;


개발환경
OS : Windows 7 pro
Eclipse : Indigo Service Release 1 Build id: 20110916-0149
Java : jdk7


왜 개발환경을 쓴고하니... 인터넷에 찾아보니깐 갖가지 방법들이 나왔는데
내 환경에서는 되지 않았다. 클래스와 android.jar사이 구분자가 ";"로 하니 되는데
어느 블로그에는 ":"로 했더니 되더란다.. 환경은 리눅스환경인듯.. 아무튼
윈도우 환경에서는 아래 코드를 넣고 실행하니 헤더가 생성되었다~~ 아싸...ㅡㅡ;;


D:\project\NDKExam>javah -classpath bin/classes;D:\MyDevelopment\Android\android-sdk\platforms\android-8\android.jar org.example.ndk.NDKExam




아.. 참고로 책에는 이렇게 나와있었다.. 윈도우환경이었다.
H:\project\NDKExam>javah -classpath bin org.example.ndk.NDKExam

2011년 11월 11일 금요일

[JNI] C에서 Java호출하기

// JniTest.java
public class JniTest{
public static void main(String[] args){
System.out.print("Hello World " + args[0]);
}
}

// 빌드는 아래와 같이 하면 JniTest.class 파일이 생성된다.
>javac JniTest.java



// JniCall.c

#include

#define PATH_SEPARATOR ';'
#define USER_CLASSPATH "."

void main(){
JNIEnv *env;
JavaVM *jvm;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jclass stringClass;
jobjectArray args;

JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString = "-Djava.class.path=" USER_CLASSPATH;
vm_args.version = 0x00010002; // 버전정보를 입력한다.
vm_args.options = options; // 옵션할당
vm_args.nOptions = 1; // 옵션의 아규먼트 갯수를 지정한다. 하나이므로 1
vm_args.ignoreUnrecognized = JNI_TRUE; // 자바가상머신이 옵션값이 잘못되어도 무시하고 진행.. 반대는 JNI_FALSE

/* Create the Java VM */
// 자바 가상머신 생성.. env주소를 넘기는건 여기 담아주셔요~~라고하는 것
// env에는 JNI 인터페이스 포인터 주소가 저장된다.
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}

// JniTest 클래스를 로드한다.
cls= (*env)->FindClass(env, "JniTest"); // 빌드된 JniTest.class가 있어야 겠죠.
if (cls == 0) {
fprintf(stderr, "Can't find JniTest class\n");
exit(1);
}
// 자바 소스에서 main함수 ID를 가져온다.
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");

// String jstr = "from C!"; 이녀석이랑 똑같음.
jstr = (*env)->NewStringUTF(env, "from C!");

// String 클래스를 가져온다.
stringClass = (*env)->FindClass(env, "java/lang/String");

// 자바 메인함수가 main(String[] args) 이렇게 구성되어있으니..
// 똑같이 배열로 오브젝트를 구성한다.
args = (*env)->NewObjectArray(env, 1, stringClass, jstr);

// main함수를 호출합니다!
(*env)->CallStaticVoidMethod(env, cls, mid, args);

// 예외처리
if((*env)->ExceptionOccurred(env)){
(*env)->ExceptionDescribe(env);
}

(*jvm)->DestroyJavaVM(jvm);
}


// 빌드는 반드시 Visual Studio Command Prompt를 실행하여 아래 명령어를 실행한다.
cl -I"D:\MyDevelopment\Android\Java\jdk1.7.0\include" -I"D:\MyDevelopment\Android\Java\jdk1.7.0\include\win32" invocationApi.c -link "D:\MyDevelopment\Android\Java\jdk1.7.0\lib\jvm.lib"

// invocationApi.exe 파일이 생성된다.



// 빌드가 완료되었으니 실행해보자!
>invocationApi.exe
Hello World from C!

// 위와 같이 출력되면 OK!


[JNI] Java에서 C 호출하기

// HelloJNI.java
class HelloJNI
{
//네이티브 메서드 선언
native void printHello();
native void printString(String str);

// library loading
static { System.loadLibrary("hellojni"); }

public static void main(String args[])
{
HelloJNI myJNI = new HelloJNI();
myJNI.printHello();
myJNI.printString("Hello World from printString fun");
}
}
// 빌드는 아래와 같이 진행
>javac HelloJNI.java
// HelloJNI.class 파일이 생성된다.



// HelloJNI.h

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

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: printHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printHello
(JNIEnv *, jobject);

/*
* Class: HelloJNI
* Method: printString
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloJNI_printString
(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif



// hellojni.c
#include "HelloJNI.h"
#include

JNIEXPORT void JNICALL Java_HelloJNI_printHello (JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}

JNIEXPORT void JNICALL Java_HelloJNI_printString (JNIEnv *env, jobject obj, jstring string)
{
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s!\n", str);
return;
}


// 빌드는 아래와 같이 진행
>cl -I"D:\MyDevelopment\Android\Java\jdk1.7.0\include" -I"D:\MyDevelopment\Android\Java\jdk1.7.0\include\win32" -LD hellojni.c -Fehellojni.dll
// Fehellojni.dll 파일이 생성된다.




// 빌드가 완료되었으니 이제 실행해보자!
>java HelloJNI
Hello World!
Hello World from printString fun!

// 위 처럼 출력결과가 나타난다.(실행된 코드 그대로 올렸으니 소스상 문제는 없을것이다.)

[JNI] 개발 환경 설정

[개발환경]
1. http://www.microsoft.com/express/download/ 경로에서
Visual Studio 2010 Express Professional 버전을 설치한다.
반드시 2010일 필요는 없음

2. 설치가 완료되면 Visual Studio Command prompt 실행

JNI 소스 빌드시 2번에서 실행한 커맨드 창에서 명령어 입력해서 빌드해야한다.