몇년 만에 블로그에 손을 대게 되다니;;;;;;
이번 삽질을 통해 몇 가지 알게 된 사실입니다.
1. JNI 함수를 만들 때 매개변수의 개수와 순서에 유의해야 한다.
첫번째 매개변수는 JNIEnv, 두번째 매개변수는 jclass 또는 jobject 여야 한다.
세번째 부터는 사용자가 JNI에서 제공되는 data type 내에서 마음껏(?) 지정할 수 있다.
2. JNI 함수에서 기본 data type의 array(예> int[], byte[] 등)가 아닌 ArrayList를 사용해야 할 땐 다음와 같이 한다.아래 코드는 2중 ArrayList(?)의 형태를 띈 ArrayList<AAA>의 최하위 요소인 test1, test2를 가져오는 코드이다.
- // ---------- AAA.java ----------
- public class AAA {
- public int itemID;
- public ArrayList<BBB> bbb;
- }
- // ---------- BBB.java ----------
- public class BBB {
- public int test1;
- public int test2;
- }
- // ---------- JNI 함수 호출을 위한 Java Interface ----------
- public class Natives {
- public static native void JNICallTest(ArrayList<AAA> list);
- }
---------- JNI 함수 구현부 ---------
- JNIEXPORT void JNICALL Java_com_test_app_jni_Natives_JNICallTest(JNIEnv *env, jobject thisObj, jobject objArrayListAAA)
- {
- // ArrayList<AAA> 형식의 클래스 가져오기
- jclass clsArrayListAAA = env->FindClass("java/util/ArrayList");
- // ArrayList의 멤버함수인 size, get을 사용하기 위해 methodID를 가져오기
- // 아래에서 얻어온 메소드ID로 ArrayList<AAA>와 ArrayList<BBB> 등, 모든 ArrayList<type> 형식의 size, get 함수 사용이 가능하다.
- jmethodID midSize = env->GetMethodID(clsArrayListAAA, "size", "()I");
- jmethodID midGet = env->GetMethodID(clsArrayListAAA, "get", "(I)Ljava/lang/Object;");
- // ArrayList<AAA>에서 AAA 클래스 가져오기
- jobject objAAA = env->CallObjectMethod(objArrayListAAA, midGet, 0);
- jclass clsAAA = env->GetObjectClass(objAAA);
- // AAA 클래스의 필드 가져오기
- // 필드의 값을 얻어오는 게 아니라, 필드 ID를 얻어와서 나중에 필드의 값을 얻어올 때 사용한다.
- jfieldID fidItemID = env->GetFieldID(clsAAA, "itemID", "I");
- jfieldID fidArrayListBBB = env->GetFieldID(clsAAA, "bbb", "Ljava/util/ArrayList;");
- jobject objArrayListBBB = env->GetObjectField(objAAA, fidArrayListBBB);
- // ArrayList<BBB> 에서 BBB 클래스 가져오기
- jobject objBBB = env->CallObjectMethod(objArrayListBBB, midGet, 0);
- jclass clsBBB = env->GetObjectClass(objBBB);
- // BBB 클래스의 필드 가져오기
- jfieldID fidTest1 = env->GetFieldID(classBBB, "test1", "I");
- jfieldID fidTest2 = env->GetFieldID(classBBB, "test2", "I");
- // 중첩 for문으로 ArrayList<AAA>의 모든 하위정보 가져오기
- int lengthAAA = env->CallIntMethod(objArrayListAAA, midSize);
- for(int ii = 0; ii < lengthAAA; ii++)
- {
- jobject _objAAA = env->CallObjectMethod(objArrayListAAA, midGet, ii);
- int itemID = env->GetIntField(_objAAA, fidItemID);
- jobject _objArrayListBBB = env->GetObjectField(_objAAA, fidArrayListBBB);
- int lengthBBB = env->CallIntMethod(_objArrayListBBB, midSize);
- for(int jj = 0; jj < lengthBBB; jj++)
- {
- jobject _objBBB = env->CallObjectMethod(_objArrayListBBB, midGet, ii);
- int test1 = env->GetIntField(_objBBB, fidTest1);
- int test2 = env->GetIntField(_objBBB, fidTest2);
- // 이런 loop구조에서는 반드시 해제해주어야 함!!!
- // 굳이 loop가 아니어도 함수 내에서 많은 양의 jobject나 jclass등을
- // 가져와서 사용해야 한다면 사용이 끝난 것들은 해제해주는 것이 좋다.
- env->DeleteLocalRef(_objBBB);
- }
- // loop 안에서 가져온 것들은 반드시 해제!!
- // 나중에 Release<type>ArrayElements 형식의 함수와 같이 사용하게 됐을 때
- // 덜 헷갈리려면 가져올 때와 역순으로 해제해주는 것이 좋다.
- env->DeleteLocalRef(_objArrayListBBB);
- env->DeleteLocalRef(_objAAA);
- }
- // 이 아래 것들은 함수가 끝나면 자동으로 해제되므로
- // 굳이 해제해주지 않아도 되지만 왠지 모를 찝찝함 때문에 해제 ^^;;
- env->DeleteLocalRef(clsBBB);
- env->DeleteLocalRef(objBBB);
- env->DeleteLocalRef(objArrayListBBB);
- env->DeleteLocalRef(clsAAA);
- env->DeleteLocalRef(objAAA);
- env->DeleteLocalRef(clsArrayListAAA);
- }
'팁 > Android' 카테고리의 다른 글
안드로이드 프로그래밍에서 OnClickListener 강제발생 시키기 (1) | 2010.08.05 |
---|