【发布时间】:2016-03-25 11:38:59
【问题描述】:
我正在尝试使用 JNI 从 c++ 代码中调用一些 java 类。今天我在我的程序中遇到了一个非常奇怪的行为。我怀疑c++代码没有等到java端完成它的工作,我不知道为什么。
C++ 代码(在共享对象库中)在它自己的 C++ 线程中运行。它正在使用已经启动并运行的 Java 应用程序的现有 JavaVM。在 Java 应用程序在 JNI_Onload 中加载共享对象库时获取的对 VM 和 ClassLoader 的引用。这里我调用了我在C++线程中用JNI创建的一个java对象的java方法:
env->CallVoidMethod(javaClassObject, javaReceiveMethod, intParam, byteParam, objectParam);
uint32_t applicationSideResponseCode = getResponseCode(objectClass, objectParam, env);
resp.setResponseCode(applicationSideResponseCode);
std::string applicationData = getApplicationData(serviceResultClass, serviceResultObject, env);
resp.setData(applicationData);
Java javaReceiveMethod 正在访问数据库并获取一些存储在objectParam 中的applicationData。不幸的是,C++ 代码在 java 类完成它的工作之前获取了 applicationData。 applicationData 是null,JNI 崩溃。为什么?我找不到任何说明 CallVoidMethod 是异步执行的 Oracle 文档?
编辑
我确认java方法没有发生异常。一切似乎都很好,只是在 C++ 尝试访问数据时 Java 仍然很忙。
编辑
我可以确认,如果我调试 java 应用程序,会显示两个线程。在主线程上,正在执行javaReceiveMethod 和一个正在获取applicationData 的线程。我怎么解决这个问题?在第二个线程空闲直到数据可用?
编辑
在我的 C++ 代码中,我正在创建一个要调用的 java 类的新对象:
jmethodID javaClassConstructor= env->GetMethodID(javaClass, "<init>", "()V");
jobject serviceObject = env->NewObject(javaClass, serviceConstructor);
jobject javaClassObject = env->NewObject(javaClass, javaClassConstructor);
之后我调用如上所示的代码。经过更多调试后,我可以说该方法是在名为Thread-2 的线程中调用的(我不知道那是c++ 线程还是来自JNI 的新线程)。绝对不是java主线程。然而,该方法的工作被中断。这意味着如果我调试代码,我可以看到数据很快就会设置,但在下一个调试步骤中会执行 getApplicationData 方法(只有在 c++ 调用它时才会发生)。
编辑 我调用的Java方法:
public int receive(int methodId, byte[] data, ServiceResult result){
log.info("java enter methodID = " + methodId + ", data= " + data);
long responseCode = SEC_ERR_CODE_SUCCESS;
JavaMessageProto msg;
try {
msg = JavaMessageProto (data);
log.info("principal: " + msg.getPrincipal());
JavaMessage message = new JavaMessage (msg);
if(methodId == GET_LISTS){
//this is shown in console
System.out.println("get lists");
responseCode = getLists(message);
//this point is not reached
log.info("leave");
}
//[... different method calls here...]
if(responseCode != SEC_ERR_CODE_METHOD_NOT_IMPLEMENTED){
//ToDoListMessageProto response = message.getProtoBuf();
JavaMessageProto response = JavaMessageProto.newBuilder()
.setToken(message.getToken())
.setPrincipal(message.getPrincipal()).build();
byte[] res = response.toByteArray();
result.setApplicationData(response.toByteArray());
}
else{
result.setApplicationData("");
}
} catch (InvalidProtocolBufferException e) {
responseCode = SEC_ERR_CODE_DATA_CORRUPTED;
log.severe("Error: Could not parse Client message." + e.getMessage());
}
result.setResponseCode((int)responseCode);
return 0;
}
第二种方法是
public long getLists(JavaMessage message) {
log.info("getLists enter");
String principal = message.getPrincipal();
String token = message.getToken();
if(principal == null || principal.isEmpty()){
return SEC_ERR_CODE_PRINCIPAL_EMPTY;
}
if(token == null || token.isEmpty()){
return SEC_ERR_CODE_NO_AUTHENTICATION;
}
//get user object for authorization
SubjectManager manager = new SubjectManager();
Subject user = manager.getSubject();
user.setPrincipal(principal);
long result = user.isAuthenticated(token);
if(result != SEC_ERR_CODE_SUCCESS){
return result;
}
try {
//fetch all user list names and ids
ToDoListDAO db = new ToDoListDAO();
Connection conn = db.getConnection();
log.info( principal + " is authenticated");
result = db.getLists(conn, message);
//this is printed
log.info( principal + " is authenticated");
conn.close(); //no exception here
message.addId("testentry");
//this not
log.info("Fetched lists finished for " + principal);
} catch (SQLException e) {
log.severe("SQLException:" + e.getMessage());
result = SEC_ERR_CODE_DATABASE_ERROR;
}
return result;
}
【问题讨论】:
-
你能澄清你的问题的描述吗?您首先说“javaReceiveMethod”在C++线程上执行,然后您说它在主线程上执行?顺便说一句,方法调用是同步执行的。
-
我会更新我的答案,并尽可能清楚地描述它。但目前我很困惑 JNI 到底在做什么。
-
您尚未向我们展示您正在调用的 Java 方法。
-
JNI 本身不会创建新线程,因此 Thread-2 应该是您的 C++ 线程。您可以将 JavaVMAttachArgs arg 中的线程名称设置为 AttachCurrentThread() btw。
-
作为 JNI 编码的一般建议:从长远来看,为您执行所有检查的所有 JNI 函数(返回代码、ExceptionOccurred() 等)都有一个包装器确实值得如果出现问题,则抛出 C++ 异常。自 2003 年以来,我们一直在使用这样的包装器,它不止一次地拯救了我们。
标签: java c++ java-native-interface