我正在尝试构建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方法,但不是我定义的方法?我还没有试过制作任何静态但我不确定这是否会有所帮助。
答案 0 :(得分:1)
您的问题的答案在于您的原生方法是否是静态的。
在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!";
}
}
原生代码:
#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
将失败,因为它是一个类,而不是AgentMgrJni
的实例。
另请注意,我通过ReleasingUTFChars
修复了内存泄漏..以及其他错误处理问题。我还在停止函数中调用DeleteGlobalRef
..
答案 1 :(得分:0)
const char* str = (*env)->GetStringUTFChars(env, strObj, NULL);
printf("\nCalling class is: %s\n", str);
//this prints "class com.example.AgentMgrService"
打印调用_jamgr_appObj.toString()
的结果。这是"This is a string!"
。肯定是不 "class com.example.AgentMgrService"
,无论它打印什么,肯定不是'呼叫类'。你在这里寻找getClass().toString()
吗?
否则这不是真正的代码。这看起来更有可能。
//this returns NULL
它返回零。 methodIDs
不是指针。
// ... and thus the below call fails
(*env)->CallVoidMethod(env, _jamgr_appObj, mid, jstr);
为什么你不只是传递原始的thiz
对象,而JNIEnv*
直接传递给jni_callback()
对我来说是一个谜。您当然不需要GlobalRef
或致电String.toString()
。
此代码中缺少错误检查。 每次 JNI调用后都必须检查其结果,如果错误,则必须使用异常检查或打印或抛出方法之一。而不是像错误那样继续进行。