Java Android JNI GetMethodID()失败
我正在尝试构建一个Cordova插件,以支持在Ionic Cordova应用程序中使用的C库。到目前为止,JavaScript→Java和Java→我的代码工作的C部分。我可以在手机上运行的Android Studio中成功调试C代码。但是,我的C库有一个回调方法,需要向上传递堆栈(C→JAVA→JavaScript),我在如何让JNI方法正常工作方面遇到了问题。以下是我目前的代码: AgentMgrService.JavaJava Android JNI GetMethodID()失败,java,android,c,cordova,java-native-interface,Java,Android,C,Cordova,Java Native Interface,我正在尝试构建一个Cordova插件,以支持在Ionic Cordova应用程序中使用的C库。到目前为止,JavaScript→Java和Java→我的代码工作的C部分。我可以在手机上运行的Android Studio中成功调试C代码。但是,我的C库有一个回调方法,需要向上传递堆栈(C→JAVA→JavaScript),我在如何让JNI方法正常工作方面遇到了问题。以下是我目前的代码: AgentMgrService.Java package com.example; import ... p
package com.example;
import ...
public class AgentMgrService {
private static final String TAG = "AgentMgrService";
private boolean libLoaded = false;
private static Context mContext;
public CallbackContext jsCallback;
// C-function interface
public static native void startAgentMgr(String agentMgrConfig);
public static native void stopAgentMgr();
// load library
static {
System.loadLibrary("lib_agentmgr");
}
public AgentMgrService(Context context) {
mContext = context;
}
public void startMobileAgentMgr(String agentmgrConfig) throws RemoteException {
startAgentMgr(agentmgrConfig);
public void testMe() {
Log.d(TAG, "testMe!");
}
public String toString() {
Log.d(TAG, "This is a string!");
return "This is a string!";
}
}
AgentMgrJni.c
#include ...
static JavaVM* _jamgr_appVm = NULL;
static jobject _jamgr_appObj = NULL;
void
Java_com_example_AgentMgrService_startAgentMgr(
JNIEnv* env,
jobject thiz,
jstring config_data)
{
if (_jamgr_appObj == NULL) {
_jamgr_appObj = (*env)->NewGlobalRef(env, thiz);
}
//... Stuff happens here ...
jni_callback();
}
int
jni_callback()
{
JNIEnv* env = NULL;
jint retval = 0;
jmethodID mid = NULL;
jclass cls = NULL;
retval = (*_jamgr_appVm)->GetEnv(_jamgr_appVm, (void**) &env, JNI_VERSION_1_6);
cls = (*env)->GetObjectClass(env, _jamgr_appObj);
//Try the toString() method
mid = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;");
jobject strObj = (*env)->CallObjectMethod(env, _jamgr_appObj, mid);
const char* str = (*env)->GetStringUTFChars(env, strObj, NULL);
printf("\nCalling class is: %s\n", str);
//this prints "class com.example.AgentMgrService"
mid = (*env)->GetMethodID(env, cls, "testMe", "()V");
//this returns NULL and thus the below call fails
(*env)->CallVoidMethod(env, _jamgr_appObj, mid, jstr);
return retval;
}
运行上述代码时,在第一个GetMethodID()
出现之前,一切正常。调用toString()
时,我得到了样板文件“class com.example.AgentMgrService”
。但是等等,我重载了toString()
!此外,尝试获取testMe()
会返回NULL,因此无法找到该方法。所以我在正确的类中,实际上可以从C中调用一些Java方法,但不能调用我定义的方法?我还没有尝试过使任何东西静止,但我不确定这是否会有帮助
const char* str = (*env)->GetStringUTFChars(env, strObj, NULL);
printf("\nCalling class is: %s\n", str);
//this prints "class com.example.AgentMgrService"
它打印调用\u jamgr\u appObj.toString()
的结果。这是“这是一个字符串!”
。它肯定不是“class com.example.AgentMgrService”
,它打印的任何内容肯定不是“调用类”。您正在这里查找getClass().toString()
否则这不是真正的代码。这似乎更有可能
//this returns NULL
它返回零<代码>方法ID不是指针
// ... and thus the below call fails
(*env)->CallVoidMethod(env, _jamgr_appObj, mid, jstr);
为什么您不直接将原始的thiz
对象和JNIEnv*
传递到jni\u callback()
,这对我来说是个谜。您当然不需要使用GlobalRef
或调用String.toString()
这段代码中缺少错误检查,令人遗憾。每个JNI调用之后都必须检查其结果,如果错误,则必须使用异常检查或打印或抛出方法之一。而不是像没有发生错误一样继续操作。问题的答案在于本机方法是否是静态的 在JNI,如果你有
public static native void startAgentMgr(String agentMgrConfig);
public static native void stopAgentMgr();
在java方面,当调用此方法时,this
对象将是一个类而不是实例。。毕竟,这个方法是静态的,它没有这个
但是,如果将其更改为(注意缺少static
关键字):
然后,当您运行代码时,此
参数将是调用此方法的对象的实例
例如:
package com.example.brandon.test;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
public class AgentMgrService {
private static final String TAG = "AgentMgrService";
// load library
static {
System.loadLibrary("lib_agentmgr");
}
// C-function interface
public native void startAgentMgr(String agentMgrConfig);
public native void stopAgentMgr();
public AgentMgrService(Context context) {
}
public void startMobileAgentMgr(String agentmgrConfig) throws RemoteException {
startAgentMgr(agentmgrConfig);
}
public void testMe() {
Log.d(TAG, "testMe!");
}
@Override
public String toString() {
Log.d(TAG, "This is a string!");
return "This is a string!";
}
}
本机代码:
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static JavaVM* _jamgr_appVm = NULL;
static jobject _jamgr_appObj = NULL;
int jni_callback();
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* aReserved)
{
_jamgr_appVm = vm;
return JNI_VERSION_1_4;
}
JNIEXPORT void JNICALL
Java_com_example_brandon_test_AgentMgrService_startAgentMgr(
JNIEnv* env,
jobject thiz,
jstring config_data)
{
if (_jamgr_appVm == NULL)
{
(*env)->GetJavaVM(env, *_jamgr_appVm);
}
if (_jamgr_appObj == NULL) {
_jamgr_appObj = (*env)->NewGlobalRef(env, thiz);
}
jni_callback();
}
JNIEXPORT void JNICALL
Java_com_example_brandon_test_AgentMgrService_stopAgentMgr(
JNIEnv* env,
jobject thiz,
jstring config_data)
{
(*env)->DeleteGlobalRef(env, _jamgr_appObj);
_jamgr_appObj = NULL;
}
int jni_callback()
{
JNIEnv* env = NULL;
jint retval = (*_jamgr_appVm)->GetEnv(_jamgr_appVm, (void**) &env, JNI_VERSION_1_4);
if (retval == JNI_OK)
{
jclass cls = (*env)->GetObjectClass(env, _jamgr_appObj);
if (cls)
{
jmethodID mid = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;");
if (mid)
{
jobject strObj = (*env)->CallObjectMethod(env, _jamgr_appObj, mid);
if (strObj)
{
const char *str = (*env)->GetStringUTFChars(env, strObj, NULL);
printf("\nCalling class is: %s\n", str);
(*env)->ReleaseStringUTFChars(env, strObj, str);
strObj = NULL;
}
mid = (*env)->GetMethodID(env, cls, "testMe", "()V");
if (mid)
{
(*env)->CallVoidMethod(env, _jamgr_appObj, mid);
}
}
}
}
return retval;
}
#包括
#包括
#包括
#包括
静态JavaVM*\u jamgr\u appVm=NULL;
静态作业对象_jamgr_appObj=NULL;
int jni_回调();
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM*vm,void*aReserved)
{
_jamgr_appVm=vm;
返回JNI_版本_1_4;
}
JNIEXPORT void JNICALL
Java\u com\u示例\u brandon\u测试\u代理GRService\u startAgentMgr(
JNIEnv*env,
jobject thiz,
jstring配置(数据)
{
if(_jamgr_appVm==NULL)
{
(*env)->GetJavaVM(env,*\u jamgr\u appVm);
}
如果(_jamgr_appObj==NULL){
_jamgr_appObj=(*env)->NewGlobalRef(env,thiz);
}
jni_回调();
}
JNIEXPORT void JNICALL
Java\u com\u示例\u brandon\u test\u AgentMgrService\u stopAgentMgr(
JNIEnv*env,
jobject thiz,
jstring配置(数据)
{
(*env)->DeleteGlobalRef(env,_jamgr_appbj);
_jamgr_appObj=NULL;
}
int jni_回调()
{
JNIEnv*env=NULL;
jint retval=(*\u jamgr\u appVm)->GetEnv(\u jamgr\u appVm,(void**)和env,JNI\u版本1\u 4);
如果(retval==JNI_OK)
{
jclass cls=(*env)->GetObjectClass(env,_jamgr_appbj);
如果(cls)
{
jmethodID mid=(*env)->GetMethodID(env,cls,“toString”,“()Ljava/lang/String;”;
如果(中)
{
jobject strObj=(*env)->CallObjectMethod(env,jamgr,appObj,mid);
if(strObj)
{
const char*str=(*env)->GetStringUTFChars(env,strObj,NULL);
printf(“\n装入类为:%s\n”,str);
(*env)->释放StringUTFChars(env、strObj、str);
strObj=NULL;
}
mid=(*env)->GetMethodID(env,cls,“testMe”,“V”);
如果(中)
{
(*env)->CallVoidMethod(env,jamgr,appObj,mid);
}
}
}
}
返回返回;
}
这将满足您的需要,因为JNI方法在Java端不是静态的。但是,如果将它们设置为静态,那么getMethodID
将无法工作,因为thiz是一个类,而不是AgentMgrJni
的实例
还请注意,我通过ReleasingUTFChars
修复了内存泄漏。。以及其他错误处理问题。我还在stop函数中调用了DeleteGlobalRef
。如果它确实打印了“class com.example.AgentMgrService”
,您想让我拍摄控制台/调试器的屏幕截图吗?这是真实的、编译过的代码,但这是一个测试,我运行它是为了模拟在本机Android应用程序(即不是Ionic Cordova)上正常工作的行为。由于实际库的其余部分的设置方式,全局引用是必需的jni_callback()
不是直接从Java调用方调用的,它发生在大约30秒后,在一堆其他工作线程被激发并完成它们的工作之后。。。。。。事实上,我甚至不确定调用jni\u callback()
的线程是否与Java端启动的线程相同。我知道代码是空的,并且缺少错误检查,我只是想理解为什么我无法从jni\u callback()
中获取AgentMgrService.testMe()
,因为它返回零。我现在真的不在乎错误检查
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static JavaVM* _jamgr_appVm = NULL;
static jobject _jamgr_appObj = NULL;
int jni_callback();
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* aReserved)
{
_jamgr_appVm = vm;
return JNI_VERSION_1_4;
}
JNIEXPORT void JNICALL
Java_com_example_brandon_test_AgentMgrService_startAgentMgr(
JNIEnv* env,
jobject thiz,
jstring config_data)
{
if (_jamgr_appVm == NULL)
{
(*env)->GetJavaVM(env, *_jamgr_appVm);
}
if (_jamgr_appObj == NULL) {
_jamgr_appObj = (*env)->NewGlobalRef(env, thiz);
}
jni_callback();
}
JNIEXPORT void JNICALL
Java_com_example_brandon_test_AgentMgrService_stopAgentMgr(
JNIEnv* env,
jobject thiz,
jstring config_data)
{
(*env)->DeleteGlobalRef(env, _jamgr_appObj);
_jamgr_appObj = NULL;
}
int jni_callback()
{
JNIEnv* env = NULL;
jint retval = (*_jamgr_appVm)->GetEnv(_jamgr_appVm, (void**) &env, JNI_VERSION_1_4);
if (retval == JNI_OK)
{
jclass cls = (*env)->GetObjectClass(env, _jamgr_appObj);
if (cls)
{
jmethodID mid = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;");
if (mid)
{
jobject strObj = (*env)->CallObjectMethod(env, _jamgr_appObj, mid);
if (strObj)
{
const char *str = (*env)->GetStringUTFChars(env, strObj, NULL);
printf("\nCalling class is: %s\n", str);
(*env)->ReleaseStringUTFChars(env, strObj, str);
strObj = NULL;
}
mid = (*env)->GetMethodID(env, cls, "testMe", "()V");
if (mid)
{
(*env)->CallVoidMethod(env, _jamgr_appObj, mid);
}
}
}
}
return retval;
}