java - Android JNI GetMethodID() 失败

标签 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 ...

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/

相关文章:

java - 云端数值计算环境? [ 本科项目 ]

c - 如何解决警告: extra tokens at end of#undef directive [enabled by default]

c++ - 交换函数 - 指针 - 混淆

java - 在 Eclipse 中不建议向 JPanel 添加标签和添加方法

java - 如何格式化 Java 中 TreeMap 中的字符串列表以这种方式打印?

java - 判断输入文本文件中的行是否全部大写,java

android - 光标 movetofirst() 和空指针异常

android - 下拉动画

javascript - 我如何开始通过 Tasker 使用 Endomondo 进行跟踪?

c - 打印刚刚初始化的变量时未定义的行为