我正在尝试构建一个 Cordova 插件来支持在 Ionic Cordova 应用程序中使用的 C 库。到目前为止,我的代码的 JavaScript→Java 和 Java→C 部分有效。我可以在手机上运行的 Android Studio 中成功调试 C 代码。但是,我的 C 库有一个需要向上传递堆栈的回调方法 (C→Java→JavaScript),我在让 JNI 方法正常工作时遇到了问题。到目前为止,这是我的代码:
AgentMgrService.Java
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 方法,但不能调用我定义的方法?我还没有尝试将任何东西设为静态,但我不确定这是否有帮助。
最佳答案
你的问题的答案在于你的本地方法是否是静态的..
在 JNI 中,如果你有
public static native void startAgentMgr(String agentMgrConfig);
public static native void stopAgentMgr();
在java端,当你调用这个方法时,this
对象将是一个CLASS而不是一个INSTANCE..毕竟这个方法是静态的,它没有this
.
但是,如果您将其更改为(注意缺少 static
关键字):
public native void startAgentMgr(String agentMgrConfig);
public native void stopAgentMgr();
然后当您运行代码时,this
参数将成为调用此方法的对象的实例。
例子:
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!";
}
}
native 代码:
#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;
}
这将执行您想要的操作,因为 JNI 方法在 Java 端不是静态的。但是,如果您将它们设为静态,则 getMethodID
将无法正常工作,因为 thiz 是一个类而不是 AgentMgrJni
的实例。
另请注意,我通过 ReleasingUTFChars
修复了您的内存泄漏问题……以及其他错误处理问题。我还在停止函数中调用了 DeleteGlobalRef
..
关于java - Android JNI GetMethodID() 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46555179/