2012-03-20 27 views
8

Tôi chỉ mới bắt đầu với JNI và tôi có một vấn đề sau đây.JNI giữ một tham chiếu toàn cục đến một đối tượng, truy cập nó với các phương thức JNI khác. Giữ một đối tượng C++ còn tồn tại trên nhiều cuộc gọi JNI

Tôi có thư viện C++ có lớp đơn giản. Tôi có ba phương pháp JNI được gọi là từ dự án Java Android, instatiate nói lớp, gọi một phương thức trên lớp instantiated, và tiêu diệt nó, tương ứng. Tôi giữ một tham chiếu toàn cầu đối tượng này, vì vậy nó sẽ có sẵn cho tôi trong hai phương pháp JNI khác.

Tôi nghi ngờ rằng tôi không thể thực hiện việc này. Khi tôi chạy ứng dụng, tôi nhận được một lỗi thời gian chạy (sử dụng tài liệu tham khảo cũ), và tôi nghi ngờ rằng điều này là do việc giới thiệu toàn cầu không hợp lệ trong các cuộc gọi tiếp theo đến các phương thức JNI khác.

Là cách duy nhất để đạt được những gì tôi muốn (có đối tượng sống qua nhiều cuộc gọi JNI), để thực sự truyền lại con trỏ đến lớp được khởi tạo trở lại Java, giữ nó ở đó và sau đó chuyển nó trở lại JNI hoạt động? Nếu vậy, điều đó tốt, tôi muốn chắc chắn rằng tôi không thể làm điều đó với một tham chiếu toàn cầu, và tôi không chỉ thiếu một cái gì đó.

Tôi đã đọc tài liệu và các chương về tham chiếu toàn cầu/cục bộ trong JNI, nhưng có vẻ như chỉ áp dụng cho các lớp Java chứ không phải các lớp C++ gốc của riêng tôi hoặc tôi sai.

Đây là mã nếu mô tả của tôi là không rõ ràng (tóm tắt, tôi tự hỏi nếu cơ chế này của sự bền bỉ đối tượng sẽ làm việc tại tất cả):

Java:

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(); 
} 

}

Tiêu đề 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 body:

#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++ tiêu đề:

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

TestClass(JNIEnv *env); 

jstring getString(); 
}; 

C++ cơ thể:

#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; 
} 

Cảm ơn

Trả lời

4

Sự cố với việc triển khai của bạn là jstring thành viên dữ liệu. NewStringUTF() tạo đối tượng Java String để trả về từ phương thức JNI. Vì vậy, nó là một tài liệu tham khảo Java địa phương. Tuy nhiên, bạn đang lưu trữ điều này bên trong một đối tượng C++ và cố gắng sử dụng nó qua các cuộc gọi JNI.

Bạn nên giữ một sự phân biệt tốt hơn giữa các đối tượng C++, Java và giao diện JNI ở giữa. Nói cách khác, C++ nên sử dụng cách lưu trữ C++ (như std::string). Việc thực hiện JNI của InvokeNativeFunction() sẽ chuyển đổi thành giá trị trả về là jstring.

PS: Có trường hợp yêu cầu triển khai C++ để giữ tham chiếu đến đối tượng Java (hoặc cách khác).Nhưng nó làm cho mã phức tạp hơn và dễ bị lỗi bộ nhớ nếu không được thực hiện đúng. Vì vậy, bạn chỉ nên sử dụng nó khi nó thực sự thêm giá trị.

+0

Đúng rồi! Tôi chắc chắn vấn đề là ở nơi khác. Dù sao, giúp đỡ rất nhiều. Cảm ơn rất nhiều! – cierech

0

Bạn không thể làm điều đó. Các tham chiếu đối tượng, bao gồm các tham chiếu lớp, không hợp lệ trong các cuộc gọi JNI. Bạn cần đọc phần Đặc tả JNI về các tham chiếu cục bộ và toàn cục.

+0

Điều này có vẻ không chính xác. Tôi đã đọc phần mà bạn đang đề cập đến. Tôi không hỏi về một tham chiếu cục bộ/toàn cầu của một lớp Java trong thư viện riêng, nhưng lớp C++ của riêng tôi. Câu trả lời ở trên là chính xác. – cierech

+1

@ user1282104 Không có gì sai về nó. Bạn đang lưu trữ một chuỗi ký tự trong đối tượng C++ của bạn và lưu trữ nó trên toàn cầu. Do đó, bạn lưu trữ chuỗi trên toàn cầu, và bạn không thể làm điều đó. Tấm áp phích khác cũng nói như vậy. Bạn có thể sử dụng GlobalRef hoặc WeakRef để giữ chuỗi, hoặc, như các poster khác được đề xuất, sử dụng một cách C++ để lưu trữ chuỗi. – EJP

+0

Ok, tôi đã sửa. Lấy làm tiếc. – cierech

0

tôi không thể tìm thấy một câu trả lời tốt trên SO về chủ đề này, vì vậy đây là giải pháp của tôi để giữ đối tượng sống trên C++ để tham khảo họ từ nhiều JNI cuộc gọi:

Java

On phía Java, tôi đang tạo một lớp với một con trỏ long để giữ một tham chiếu đến đối tượng C++. Bao bọc các phương thức C++ trong một lớp Java, cho phép chúng ta sử dụng các phương thức C++ trong nhiều hoạt động. Lưu ý rằng tôi đang tạo đối tượng C++ trên hàm khởi tạo và tôi đang xóa đối tượng đang bị xóa. Đây là điều rất quan trọng để ngăn chặn rò rỉ bộ nhớ:

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++

Về phía C++, tôi xác định chức năng để tạo, chỉnh sửa và xóa các đối tượng. Điều quan trọng cần lưu ý là chúng ta phải sử dụng newdelete để lưu trữ đối tượng trong bộ nhớ HEAP để giữ cho nó tồn tại trong suốt vòng đời của các cá thể lớp Java. Tôi cũng lưu trữ các con trỏ đến CppObject thẳng trong JavaClass, sử dụng getFieldId, SetLongField, và GetLongField:

// 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; 
    } 

} 

GHI CHÚ:

  • Không giống như Java, C++ không có thu gom rác thải, và đối tượng sẽ sống trên bộ nhớ HEAP, cho đến khi bạn sử dụng delete.
  • Tôi đang sử dụng GetFieldID, SetLongFieldGetLongField để lưu trữ tham chiếu đối tượng từ C++, nhưng bạn cũng có thể lưu con trỏ đối tượng jlong từ Java như được thảo luận trên các câu trả lời khác.
  • Trên mã cuối cùng của mình, tôi đã triển khai lớp JavaObject làm Parcelable để chuyển lớp học của mình sang nhiều hoạt động bằng cách sử dụng Intent với các tính năng bổ sung.
Các vấn đề liên quan