2013-02-21 2 views
1

저는 현재 JNI 환경과 jobject 객체를 로컬로 저장했습니다. JNI에서 ICS 및 장치를 실행하려면 JNI 코드를 수정해야합니다. 심지어 바로 그 일을하고있는 경우JNIEnv 글로벌 참조가 C의 jobject와 다른 점은 무엇입니까?

02-20 10:20:59.523: E/dalvikvm(21629): JNI ERROR (app bug): attempt to use stale local reference 0x38100019 
02-20 10:20:59.523: E/dalvikvm(21629): VM aborting 
02-20 10:20:59.523: A/libc(21629): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 21629 

내가/만들 전역을 파괴하는 방법에 대한 혼란 스러워요, 그리고이 오류가 내가 얻을 수있다.

내 응용 프로그램이 현재이 코드를 사용하여 모든 사전 ICS 장치에서 잘 실행

: 마지막으로

void WrapSetBaud(WORD w) { 
    return (*myEnv)->CallStaticVoidMethod(myEnv, myObject, changeID, w); 
} 

short WrapGetIt(WORD time) { 
    return (*myEnv)->CallStaticShortMethod(myEnv, myObject, getID, time); 
} 

void WrapPutIt(BYTE buff) { 
    return (*myEnv)->CallStaticVoidMethod(myEnv, myObject, putID, buff); 
} 

void WrapFlushIt(void) { 
    return (*myEnv)->CallStaticVoidMethod(myEnv, myObject, flushID); 
} 

void WrapDelayIt(WORD wait) { 
    return (*myEnv)->CallStaticVoidMethod(myEnv, myObject, delayID, wait); 
} 

다음 GetStaticMethodID 통화에서

BYTE Java_my_eti_commander_RelayAPIModel_00024NativeCalls_InitRelayJava(JNIEnv *env, jobject obj ) { 

    myEnv = (env); 
    myObject = obj; 

    changeID = (*myEnv)->GetStaticMethodID(myEnv, myObject, "changeItJavaWrapper", "(S)V" ); 
    getID = (*myEnv)->GetStaticMethodID(myEnv, myObject, "getItJavaWrapper" , "(S)S" ); 
    putID = (*myEnv)->GetStaticMethodID(myEnv, myObject, "putItJavaWrapper" , "(B)V"); 
    flushID = (*myEnv)->GetStaticMethodID(myEnv, myObject, "flushItJavaWrapper" , "()V" ); 
    delayID = (*myEnv)->GetStaticMethodID(myEnv, myObject, "delayItJavaWrapper" , "(S)V" ); 

    RelayAPI_SetBaud= WrapSetBaud; 
    RelayAPI_get = WrapGetIt; 
    RelayAPI_put = WrapPutIt; 
    RelayAPI_flush = WrapFlushIt; 
    RelayAPI_delay = WrapDelayIt; 
    ... 
} 

을의 RelayAPI_ 변수는 여기에 이어질 모든 함수 포인터는 , 여기 내 Java 코드로 돌아갑니다.

public static void changeItJavaWrapper(short l) throws IOException { 
    mModelService.changeitJava(l); 
} 

public static void flushItJavaWrapper() { 
    mModelService.flushitJava(); 
} 

public static void putItJavaWrapper(byte p) { 
    mModelService.putitJava(p); 
} 

public static void delayItJavaWrapper(short wait) { 
    mModelService.delayitJava(wait); 
} 

public static short getItJavaWrapper(short s) throws IOException { 
    return mModelService.getitJava(s); 
} 

내 초기화를 변경 :

myEnv = (*env)->NewGlobalRef(env,obj); 
myObject = (*env)->NewGlobalRef(env,obj); 

그러나이 같은 매개 변수를 가지고 내가이 극도로 혼란 스러워요, 그것은 단지 이해가되지 않습니다. this tutorial, this pagethe oracle docs은 그 방법과 관련하여 아무런 정보가 없으므로이 방법에 대한 설명서를 찾을 수 없습니다. 모든

편집

jmethodID changeID; 
jmethodID getID; 
jmethodID putID; 
jmethodID flushID; 
jmethodID delayID; 
jobject myObject; 
jclass bluetoothClass; 
JNIEnv *myEnv; 

답변

7

첫째 : myEnv = (*env)->NewGlobalRef(env,obj);은 잘못된 것입니다. You mustn't cache this value.

메소드 ID, 필드 ID, 클래스 참조 등을 캐시 할 수는 있습니다 (하지만 나중에 이걸 정리해야합니다). 그러나 이러한 값을 캐시하려면 특별한 조치가 필요합니다.

왜? 문제는 JVM이 프로그램의 필요에 따라 클래스를로드 및 언로드 할 수 있다는 것입니다. 따라서 클래스의 마지막 인스턴스가 가비지 수집기에 의해 파괴되면 클래스가 언로드됩니다. 이 문제가 발생하면 캐시 된 ID는 더 이상 유효하지 않습니다. JVM이 클래스를 다시로드 한 후에 ID가 동일 할 수도 있지만 이것이 보장되는 것은 아닙니다.

솔루션 : 이러한 ID를 캐시하려면 JVM에 클래스를 언로드 할 수 없다고 알려야합니다. 이것은 바로 NewGlobalRef의 기능입니다. NewGlobalRef에 전달 된 참조에 대한 참조를 증가 시키면 참조 수가 0이되지 않고 참조 된 요소를 정리할 수 없습니다.

주의 : 자바 외에 당신이 참조의 가비지 컬렉션을 다시 활성화하기 위해 더 이상이 참조가 필요하지 않은 경우 당신이 DeleteGlobalRef 전화 있는지 확인해야합니다하십시오 NewGlobalRef 만들기 심각한 단점이있다. (쓰레기 수거자가 아직 습기가 없다는 것을 알지 못하기 때문에이 참조가 필요한지 아닌지) 또는 다른 말로하면 : 쓰레기를 직접 청소해야합니다. 그렇지 않으면 메모리 누출이 발생합니다.

개체에 대한 전역 참조를 만드는 것이 좋지 않다고 말하고 싶습니다. (실제로 개체를 유지하고 싶지 않은 경우) 개체가 가비지에 들어 가지 않으므로 결코 될 수 없으므로 해방.

더 나은 변형 : 당신이 특정 개체에 대한 액세스 속도를 빠르게하기 위해 이러한 ID를 캐시하려면은 (FindClass 사용) 클래스에 대한 글로벌 기준을 유지하고 클래스 객체에서 FindClass 반환 ID를 잡아.

다음은 내가 의미하는 것의 (불완전한) 예입니다. 나는 보통 네임 스페이스를 깨끗하게 유지하기 위해 클래스에 액세스해야하는 모든 ID를 보유하는 구조체를 만듭니다.

/*! \brief Holds cached field IDs for MyClass.java */ 
typedef struct MyClass { 
    int loaded; /*!< Nonzero if the information are valid */ 

    jclass clazz; /*!< Holds a global ref for the class */ 
    jfieldID aField; /*!< Holds the field ID of aField */ 
}tMyClass; 

static tMyClass me = { 0 }; 

가장 쉬운 방법은 위에서 정의 된 구조의 초기화를하지 개체에 대한 "연결"기능을 제공하는 것입니다 : 다음과 같이이 상상할 수있다. MyClass_connect이 성공적으로 당신이 당신의 코드에서 필드에 액세스하는 코드를 단축 me.aField를 사용하여 호출 한 후

/*! \brief This function fetches field IDs for a specific class in order to have 
      faster access elsewhere in the code 

    \param env a valid JNI environment 

    \return 
      - 0 if OK 
      - <0 if an error occured */ 
int MyClass_connect(JNIEnv *env) 
{ 
    jobject theClass = env->FindClass("MyClass"); 
    if (theClass == NULL) goto no_class; 

    me.clazz = (jclass) env->NewGlobalRef(theClass); // make it global to avoid class unloading and therefore 
                // invalidating the references obtained. 
    if (me.clazz == NULL) goto no_memory; 

     me.aField = env->GetFieldID(me.clazz, "aField", "I") ; 
    if (me.aField == NULL) goto no_aField; 

    me.loaded = 1; 
    return 0; 

no_aField: 
    env->DeleteGlobalRef(me.clazz); 
no_memory: 
no_class: 
    return -1; 
} 

. 물론 당신은 MyClass에가 더 이상 필요하지 않은 경우 호출되는 차단 기능을 제공해야한다 :

이하지만이 당신의 혼란 비트를 해결하는 데 도움이 당신에게 비트를 제공 희망 조금 긴 게시물에 대한
void MyClass_disconnect(JNIEnv *env) 
{ 
    if (me.loaded == 0) return; 

    env->DeleteGlobalRef(me.clazz); 
    memset(me, 0, sizeof(tMyClass)); 
} 

죄송합니다 JNI의 내부 작업에 대한 통찰력과 함께 작은 receipe를 어떻게 효율적으로 처리 할 것인가.

편집 : JNI는 oracle's website

+0

덕분에 이것에 대한 많은 촉구에 대해 당신은 문서를 찾을 수 있습니다. C에서 더 많은 정보를 가지고있는 좋은 웹 사이트를 아십니까? – JuiCe

+0

정확히 무엇에 대한 정보를 찾고 있습니까? JNI? 모범 사례? – junix

+0

'GetFieldID' 대신에'GetStaticMethodID'를 사용하고 있기 때문에 아직 조금 분실했습니다. 나중에 코드에서 찾을 수있는 메서드를 호출하려면 두 번째 코드 블록에 표시된 JNI Env를 전역으로 저장해야합니다. - 또한 tUsb_Device 변수가 무엇인지 알지 못합니다. 해당 매개 변수의 숫자는 무엇과 비교됩니까? – JuiCe

관련 문제