본문 바로가기

개발 언어/JAVA

[jni]객체 필드에 접근하기


-자바에서, 객체의 변수에 접근하려면 그 객체의 이름과 변수의 이름을 알아야 한다. 마찬가지로 Native Method역시 자바 객체의 변수에 접근하기 위해서 그 객체의 타입과, 변수의 식별 정보를 컴퓨터에 인식시켜줄 필요가 있다.

※예를 들어 자바의 int형 데이터 타입의 변수에 접근하기 위해서는,Native Method에 자바의 객체 타입(클래스)과 변수의 식별 정보인 변수 이름과 변수 타입을 알려 주어야 한다.

FieldControl.java(맴버변수를 생성한 자바 소스 코드)

public class FieldControl {

   int int_a = 10;

   double double_b = -1.2345;

   static {

     System.loadLibrary("control");

   }

   public native double add();

   public static void main(String[] args) throws Exception {

       double c = new FieldControl().add();

       System.out.println("final result ==> a + b = " + c);

   }

}

C:\JavaExample\20> javac JavaObject.java

C:\JavaExample\20> javah JavaObject

 

-Native Method 내부에서 자바 객체의 변수에 접근하는 과정은 다음과 같이 세 단계로 나누어 진다.

-1단계: 자바 객체의 타입 알아오기

-2단계: 자바 객체의 타입에서 부터, 접근하려는 변수의 식별 정보를 알아오기

-3단계: 변수의 식별 정보를 사용해서, 원하는 객체의 실제 변수의 값을 변경하고 가져오기

 

자바에서 넘오는 객체의 타입은 두 가지 방법으로 알 수 있다.

-객체로 부터 그 타입을 알아내는 방법이다. 모든 Native Method가 첫 번째 매개 변수로 env라는 인터페이스 포인터를 갖는다는 것은 앞의 절에서 언급했다.

-이 인터페이스 포인터와 함께 모든 Native Method에게 주어지는 또 하나의 매개변수가 있다. 바로 jobject 타입의 obj라는 매개 변수이다.  모든 자바의 객체는 jobject라는 JNI가 미리 정의해 놓은 타입으로 표현 된다. Native Method에게 자동으로 넘겨지는 obj는 사실은 Native Method를 선언한 클래스의 객체 자신(this)이다. HelloWorld 예제의 경우라면 HelloWorld 객체 자신이 되겠군요. 다음의 함수사용해서 obj로 부터 객체의 타입을 알아 낼 수 있다. 그 원리는 리플렉션에서 getClass() 메서드를 이용해서 클래스의 정보를 클래스로더에게 알려주는 것과 똑 같다.

-jclass GetObjectClass(JNIEnv* env, jobject obj)

 

 


FieldControl.c(자바의 맴버변수를 조작하는 c언어의 소스 코드)

#include <jni.h>

#include "FieldControl.h"

#include <stdio.h>

JNIEXPORT jdouble JNICALL Java_FieldControl_add (JNIEnv *env, jobject obj){

   double c;

   jclass class_fieldcontrol = (*env)->GetObjectClass(env, obj);

   jfieldID id_a = (*env)->GetFieldID(env, class_fieldcontrol, "int_a", "I");

   jint a = (*env)->GetIntField(env,obj,id_a);

   jfieldID id_b = (*env)->GetFieldID(env, class_fieldcontrol, "double_b", "D");

   jdouble b = (*env)->GetDoubleField(env,obj,id_b);

   printf("a : %d \n", a);

   printf("b : %f \n", b);

 

   c= a+b;

   printf("before ==> a + b = %f \n", a+b);

   a += 5;

   (*env)->SetIntField(env, obj, id_a, a);

   b += 2.123;

   (*env)->SetDoubleField(env, obj, id_b, b);

   c= a+b;

   printf("after ==> a + b = %f \n", a+b);

 

   return c;

}

C:\JavaExample\20>cl -Ic:\jdk1.3.1\include -Ic:\jdk1.3.1\include\win32 -LD F

ieldControl.c -Fecontrol.dll

……

FieldControl.c

……

/dll

/implib:control.lib

/out:control.dll

FieldControl.obj

   Creating library control.lib and object control.exp

 

C:\JavaExample\object>java FieldControl

a : 10

b : -1.234500

before ==> a + b = 8.765500

after ==> a + b = 15.888500

final result ==> a + b = 15.8885

 
-자바 객체의 데이터 타입인 jclass는 다음처럼 얻어 온다.
==> jclass class_fieldcontrol = (*env)->GetObjectClass(env,obj);
 
GetObjectClass()메서드는 매개 변수로 주어진 jobject의 클래스를 jclass 형태로 반환해 주는 함수다. 이렇게 함으로써, 자바에서 생성된 객체에 접근할 수 있는 참조값을 jclass의 객체 class_fieldcontrol에게 넘겨주는 것이다. ==>자바의 참조값들은 C언어가 참조할 수 있게끔 C언어의 참조값들로 변환하여 주는 과정이다.
 
-객체의 참조값을 알아 냈기 때문에 우리는 그 참조값을 근거로 객체에 접근할 수 있다.
==>jfieldID id_a = (*env)->GetFieldID(env, class_fieldcontrol,"int_a","I");
==>jfieldID id_b = (*env)->GetFieldID(env, class_fieldcontrol, "double_b", "D");
 
-위의 코드는 GetIntField()라는 메서드에 객체변수의 참조값을 인자로 넘겨 C언어에서 사용 가능한 'jInt'라는 데이터 타입의 객체 'a'를 생성하고 있다. 이 때의 데이터 타입은 자바 객체의 데이터 타입에 따라 달라지고, 메서드의 형태 또한 달라지니 주의 하시기 바란다.
-물론 표준 C언어에는 jint라는 데이터 타입은 없다. sun사에서 임의의 데이터 타입을 만들어 헤더 파일에 정의해 놓고, 그 것을 자바에 끼워 넣은 것이다. 그래서 사용 가능한 데이터 타입들이다. 그헤더 파일이 바로 C에서 항상 include시키는 <jni.h>이고 <jni.h>를 확인해 보면 다시 "jni_md.j"를 include 시키고 있다. 그래서 우리는 컴파일 할때마다 항상 이 두헤더 파일들의 경로를 명시하는 것이다. 왜냐하면 두 헤더 파일들이 C파일과 같은 폴더에 있지 않으니깐 컴퓨터에게 알려주는 것이다.
-이제 객체변수의 데이터 타입까지 정해졌으니, 우리는 이 변수들을 C에서 SetIntField()메서드로 마음대로 제어할 수 있다. 위 예제에서는 단순히 5만을 더하는 것이다. 이 메서드를 사용할 때에도 데이터 타입에 따라 메서드의 형태가 달라 진다.
==>(*env)->SetIntField(env, obj, id_a, a);
==>(*env)->SetDoubleField(env, obj, id_b, b);
-객체의 타입인 jclass를 알아내기 위한 또 다른 방법은 다음과 같이 FindClass()함수를 사용해서 명시적으로 클래스의 이름을 제공하는 방법이다.
==>jclass class_string=(*env)->FindClass(env,"java/lang/String");
이 방법은 static변수를 가져올 때 주로 사용 된다.
변수가 속해 있는 객체의 타입을 알아 냈으면, 그 객체의 타입에서 우리가 원하는 변수는 어떤 것인지 Native Method에게 알려 주기 위해서, 클래스로 부터 변수의 식별 정보를 얻어와야 한다. 변수의 식별 정보는 jfildID라고 하는 JNI가 미리 정의한 타입을 사용한다.
변수의 식별 정보는 위에서와 같이 GetFieldID라는 함수를 사용해서 얻어올 수 있다.
 
jclass class_fieldcontrol(*env)->GetObjectClass(env,obj);

GetFieldID()메서드

JfieldID GetFieldID (JNIEnv* env, jclass cls, const char name[], const char signal[]) : 주어진 jclass 에서 특정한 이름과 타입을 갖는 필드를 가져옵니다. 반환되는 값은 자바객체의 참조값이 입니다.

jclass cls : Class 객체

const char name[] : 필드의 이름

const char signal[] : 필드의 타입 식별자

 
전 단계에서 얻어온 class_fieldcontrol의 멤버 변수들 중에서 이름이 "int_a"이고, 데이터 타입이 int인 변수의 식별정보를 가져 온다. 자바의 타입을 표현하기 위해 다음 표에 정의되어 있는 값을 사용한다.

Signature

Java Programming

Language Type

Z

boolean

B

byte

C

char

S

short

I

int

J

long

F

float

D

double

L fully-qualified-class ;

fully-qualified-class

[ type

type[]

( arg-types ) ret-type

method type

 
표 20-2 자바의 타입을 표현하기 위해 정의된 값
표를 참조해 보면 int는 "I"로 정의 되어 있다. 클래스의 경우에는 약간 특이한 룰을 가지고 있다. 우선 패키지를 포함한 클래스의 전체이름을 "." 구분자 대신 "/" 구분자를 사용해서 바꿔주고 제일 앞에는 "L"을 클래스 이름의 마지막은";"을 붙여 준다.
예를 들면 "java.lang.String"은 "Ljava/lang/String;"처럼 바뀝니다. 메서드 타입은 다음 절에서 자세히 설명 하게 하겠다네요....ㅋㅋㅋㅋ
 
객체의 타입과 변수의 식별 정보를 모두 구했으면, 실제로 변수를 조작하는 일만 남았다. JNI는 변수의 값을 변경하고 가져 오기 위해 다음과 같은 일련의 함수를 제공 한다.
 

Set/Get<type>Field

 

Set/GetBooleanField

 

Set/GetByteField

 

Set/GetCharField

 

Set/GetDoubleField

 

Set/GetFloatField

 

Set/GetIntField

 

Set/GetLongField

 

Set/GetObjectField

 

Set/GetShortField

 표 20-3 변수의 값을 변경하고 가져오기 위해 JNI에서 제공하는 함수
static 변수에 접근하는 경우에는 먼저 FindClass()함수를 사용해서 클래스를 얻어오고, 두번째 단계에서 GetFieldID()함수 대신 GetStaticFildID()를 사용해서 변수의 식별 정보를 얻어오고, 마지막으로 Set/GetStatic<type>Field()함수로 변수를 조작할 수 있다.

Static 변수를 조작하는 예

jclass class_system = (*env)->FindClasS(env, "java/lang/System");

jfieldID id_out = (*env)->GetStaticFieldID(env, class_system, "out", "Ljava/io/PrintStream;");

jobject obj_out = (*env)->GetStaticObjectField(env, class_system, id_out);

'개발 언어 > JAVA' 카테고리의 다른 글

[JNI] 배열혈 아규먼트  (0) 2011.07.22
[jni]객체의 메서드 실행하기  (0) 2011.07.22
[jni]문자열 전달 인자  (2) 2011.07.22
[jni] 숫자 인자와 변환값  (0) 2011.07.22
[JNI] 단계별 설명 (기본)  (0) 2011.07.22