2012-03-20 3 views
8

나는 JNI로 막 시작했으며 다음과 같은 문제가 있습니다.JNI는 객체에 대한 전역 참조를 유지하고 다른 JNI 메소드로 객체에 액세스합니다. 여러 JNI 호출에 걸쳐 C++ 객체 유지하기

나는 간단한 클래스를 가진 C++ 라이브러리를 가지고있다. 자바 안드로이드 프로젝트에서 3 개의 JNI 메쏘드를 호출했다. 클래스는 클래스를 선언하고 인스턴스화 된 클래스에 메소드를 호출하고 파괴한다. 이 객체에 대한 전역 참조를 유지하므로 다른 두 JNI 메소드에서 사용할 수 있습니다.

나는 이것을 할 수 없다고 생각합니다. 응용 프로그램을 실행할 때 런타임 오류가 발생했습니다 (참조가 오래됨). 다른 JNI 메서드에 대한 이후 호출에서 전역 참조가 유효하지 않기 때문에 이것이 의심스러운 것 같습니다.

내가 원하는 것 (객체가 여러 JNI 호출에 걸쳐 살아있게)을 얻는 유일한 방법은 실제로 인스턴스화 된 클래스에 대한 포인터를 Java로 다시 전달하고 거기에 보관 한 다음 다시 전달하는 것입니다. JNI는 기능합니까? 그렇다면 괜찮습니다. 전 세계적으로 참조 할 수 없도록하고 싶습니다. 뭔가 빠져있는 것이 아닙니다.

JNI의 전역/로컬 참조에 대한 설명서와 장을 읽었지만 Java 클래스에만 적용되고 내 고유 C++ 클래스에는 적용되지 않거나 잘못되었습니다.

자바 : 내 설명이 명확하지 않은 경우 다음

는 (객체를 지속이 메커니즘이 전혀 작동하는지 궁금하고, 요약) 코드입니다

package com.test.ndktest; 

import android.app.Activity; 
import android.os.Bundle; 
import android.app.AlertDialog; 

public class NDKTestActivity extends Activity { 
static { 
    System.loadLibrary("ndkDTP"); 
} 

private native void initializeTestClass(); 
private native void destroyTestClass(); 

private native String invokeNativeFunction(); 


@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    initializeTestClass(); 

    String hello = invokeNativeFunction(); 

    destroyTestClass(); 

    new AlertDialog.Builder(this).setMessage(hello).show(); 
} 

}

JNI 헤더 :

extern "C" { 

jstring Java_com_test_ndktest_NDKTestActivity_initializeTestClass(JNIEnv* env,  jobject javaThis); 
jstring Java_com_test_ndktest_NDKTestActivity_destroyTestClass(JNIEnv* env, jobject javaThis); 
jstring Java_com_test_ndktest_NDKTestActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis); 

}; 

JNI 본체 :

,
#include <string.h> 
#include <jni.h> 
#include <ndkDTP.h> //JNI header 
#include <TestClass.h> //C++ header 

TestClass *m_globalTestClass; 

void Java_com_test_ndktest_NDKTestActivity_initializeTestClass(JNIEnv* env, jobject javaThis) { 

m_globalTestClass = new TestClass(env); 
} 

void Java_com_test_ndktest_NDKTestActivity_destroyTestClass(JNIEnv* env, jobject javaThis) { 

delete m_globalTestClass; 
m_globalTestClass = NULL; 
} 


jstring Java_com_test_ndktest_NDKTestActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis) { 

jstring testJS = m_globalTestClass->getString(); 

return testJS; 

} 

C++ 헤더 :

class TestClass 
{ 
public: 
jstring m_testString; 
JNIEnv *m_env; 

TestClass(JNIEnv *env); 

jstring getString(); 
}; 

C++ 몸 :

#include <jni.h> 
#include <string.h> 

#include <TestClass.h> 

TestClass::TestClass(JNIEnv *env){ 
    m_env = env; 

    m_testString = m_env->NewStringUTF("TestClass: Test string!"); 
} 

jstring TestClass::getString(){ 
return m_testString; 
} 

감사

답변

4

구현시 문제는 jstring 데이터 멤버입니다. NewStringUTF()은 Java String 개체를 만들어 JNI 메서드에서 반환합니다. 그래서 그것은 자바 로컬 참조입니다. 그러나 이것을 C++ 객체 내에 저장하고 JNI 호출에서이 객체를 사용하려고합니다.

C++ 개체, Java 및 그 사이의 JNI 인터페이스를보다 잘 구분해야합니다. 다시 말해 C++은 문자열을 저장하는 C++ 방식 (예 : std::string)을 사용해야합니다. InvokeNativeFunction()의 JNI 구현은이를 반환 값으로 jstring으로 변환해야합니다.

추신 : 이며, C++ 구현시 Java 객체 (또는 그 반대로)에 대한 참조를 유지해야하는 경우입니다.그러나 코드가 복잡해지면 메모리 버그가 발생하기 쉽습니다. 따라서 실제로 가치를 더하는 곳에서만 사용해야합니다.

자바

켜짐 :

+0

그게 다야! 나는 그 문제가 다른 곳에 있다고 확신했다. 어쨌든, 큰 도움이됩니다. 고마워요! – cierech

0

당신은 할 수 없습니다. 클래스 참조를 포함한 객체 참조는, JNI 호출 간에는 유효하지는 않습니다. 로컬 및 글로벌 레퍼런스에 관한 JNI 스펙의 섹션을 읽어야한다.

+0

이것은 잘못된 것 같습니다. 나는 당신이 말하는 그 부분을 읽었습니다. 네이티브 라이브러리에서 Java 클래스에 대한 로컬/글로벌 참조를 묻지는 않지만 자체 C++ 클래스입니다. 위의 대답은 정확합니다. – cierech

+1

@ user1282104 잘못된 점은 없습니다. CString 객체에 jstring을 저장하고 전역으로 저장하고 있습니다. 따라서 jstring을 전역 적으로 저장하므로 그렇게 할 수 없습니다. 다른 포스터도 똑같이 말했다. jstring을 유지하기 위해 GlobalRef 또는 WeakRef를 사용할 수도 있고 다른 포스터가 제안한 것처럼 문자열을 저장하는 C++ 방식을 사용할 수도 있습니다. – EJP

+0

그래, 나는 고쳐 쓴다. 죄송합니다. – cierech

0

나는이 주제에 대한 SO에 좋은 답변을 찾을 수 없습니다, 그래서 여기에 호출 여러 JNI에서이를 참조하기 위해 C++에 살아 객체를 유지하는 내 솔루션입니다 자바 쪽, 나는 long 포인터와 함께 클래스를 생성하여 C++ 객체에 대한 참조를 유지합니다. Java 클래스에서 C++ 메소드를 래핑하면 여러 가지 활동에서 C++ 메소드를 사용할 수 있습니다. 생성자에 C++ 객체를 생성하고 있으며 객체를 정리 중에 삭제하려고합니다. 이 메모리 누수를 방지하기 위해 매우 중요합니다 다음 C++ 측면에서

public class JavaClass { 
    // Pointer (using long to account for 64-bit OS) 
    private long objPtr = 0; 

    // Create C++ object 
    public JavaClass() { 
     createCppObject(); 
    } 

    // Delete C++ object on cleanup 
    public void cleanup() { 
     deleteCppObject(); 
     this.objPtr = 0; 
    } 

    // Native methods 
    public native void createCppObject(); 
    public native void workOnCppObject(); 
    public native void deleteCppObject(); 

    // Load C++ shared library 
    static { 
     System.loadLibrary("CppLib"); 
    } 

} 

C++

, 나는 객체를 생성, 수정 및 삭제하는 기능을 정의하고있다. Heap 메모리에 객체를 저장하여 Java 클래스 인스턴스의 수명주기 동안 활성 상태로 유지하려면 newdelete을 사용해야 함을 언급하는 것이 중요합니다. 나는 또한 getFieldId, SetLongFieldGetLongField를 사용하여 JavaClassCppObject 바로 포인터를 저장하고있다 :

// Get pointer field straight from `JavaClass` 
jfieldID getPtrFieldId(JNIEnv * env, jobject obj) 
{ 
    static jfieldID ptrFieldId = 0; 

    if (!ptrFieldId) 
    { 
     jclass c = env->GetObjectClass(obj); 
     ptrFieldId = env->GetFieldID(c, "objPtr", "J"); 
     env->DeleteLocalRef(c); 
    } 

    return ptrFieldId; 
} 

// Methods to create, modify, and delete Cpp object 
extern "C" { 

    void Java_com_test_jnitest_JavaClass_createCppObject(JNIEnv *env, jobject obj) { 
     env->SetLongField(obj, getPtrFieldId(env, obj), (jlong) new CppObject); 
    } 

    void Java_com_test_jnitest_JavaClass_workOnCppObject(JNIEnv *env, jobject obj) { 
     CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj)); 

     // Write your code to work on CppObject here 
    } 

    void Java_com_test_jnitest_JavaClass_deleteCppObject(JNIEnv *env, jobject obj) { 
     CppObject* cppObj = (CppObject*) env->GetLongField(obj, getPtrFieldId(env, obj)); 

     delete cppObj; 
    } 

} 

참고 : 자바와는 달리

  • 을, C++은 가비지 수집을 가지고 있고,하지 않는 delete을 사용할 때까지 개체가 HEAP 메모리에 저장됩니다.
  • C++에서 객체 참조를 저장하기 위해 GetFieldID, SetLongFieldGetLongField을 사용하고 있지만 다른 답변에서 설명한대로 jlong 객체 포인터를 Java에서 저장할 수도 있습니다.
  • 마지막 코드에서 Intent을 엑스트라로 사용하여 여러 클래스에 클래스를 전달하기 위해 JavaObject 클래스를 Parcelable으로 구현했습니다.
관련 문제