JNI学习---C调用Java方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/johnWcheung/article/details/81383785

步骤

总体流程

Created with Raphaël 2.1.2 开始 根据jobject获取jclass(静态方法就不用这一步了) 获取jmethodid 调用方法 结束

详细步骤

  • 获取你需要访问的Java对象的类

    • 如果被Native调用的Java类是静态类:FindClass通过传java中完整的类名来查找java的class

    • 如果是非静态类:GetObjectClass通过传入jni中的一个java的引用来获取该引用的类型

    他们之间的区别是,前者要求你必须知道完整的类名,后者要求在Jni有一个类的引用

  • 获取调用方法的MethodID

    • GetMethodID 得到一个实例的方法的ID

    • GetStaticMethodID 得到一个静态方法的ID

  • 获取对象的属性

    • GetFieldID 得到一个实例的域的ID

    • GetStaticFieldID 得到一个静态的域的ID

JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数

环境

jni里面调用java方法的环境分为2种:

  • 第一种:在env所在线程调用java方法,这种情况不需要做特殊处理,直接按照步骤执行即可

  • 第二种:在pthread子线程调用java方法,这种情况下就需要做处理了

在jni中,子线程中是不能直接调用JNIEnv对象的,也不能直接调用env线程中的jobject对象。因为jni中,JNIEnv是和线程相关的,每一个native方法所在线程就有一个当前线程相关的JNIEnv对象,而pthread线程中是不能调用native方法所在线程的JENnv对象的。

解决办法是:利用JavaVM虚拟机

JavaVM是和进程相关的,一个进程里面的JavaVM都是同一个,所以在pthread线程中就可以通过JavaVM来获取(AttachCurrentThread)当前线程的JNIEnv指针,然后就可以使用JNIEnv指针操作数据了。

还有在pthread线程中调用jobject对象时,首先需要把native线程里面的jobject创建全局引用(env->NewGlobalRef(jobj)),其返还的jobject对象就可以在程序中使用了,具体的调用函数的代码和函数的返回值相关,对应规则如下:

Instance Method Calling Routines:(静态方法CallStatic...

Call<type>Method Routine Name Native Type
CallVoidMethod void
CallObjectMethod jobject
CallBooleanMethod jboolean
CallByteMethod jbyte
CallCharMethod jchar
CallShortMethod jshort
CallIntMethod jint
CallLongMethod jlong
CallFloatMethod jfloat
CallDoubleMethod jdouble

在JNI_OnLoad中获取我们需要的JavaVM指针

//hello-jnicallback.h

...
void initClassHelper(JNIEnv *env, const char *path, jobject *objptr);
int handleCmd(const char * cmd);

static JavaVM *g_jvm = NULL;//java虚拟机
static jobject gInterfaceObject = NULL;//全局对象
static const char* const kClassPathName =
"com/tcl/hello/HelloAPI";
...

Java支持函数重载,利用方法签名和方法名来唯一确定一个JNI函数的调用

Java 类型 类型签名
void V
boolean Z
byte B
char C
short S
int I
long L
float F
double D
L全限定名;,比如String, 其签名为Ljava/lang/String;
数组 [类型签名, 比如 [B

参数String类型,返回值int类型:(Ljava/lang/String;)I

int func(int, String, int[]) => (ILjava/lang/String;[I)I


//hello-jnicallback.c

...
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
    printf("-----------JNI_OnLoad-----------------");
    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        printf("ERROR: JNI_OnLoad GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);

    g_jvm = vm;

    /* success -- return valid version number */
    result = JNI_VERSION_1_6;
    initClassHelper(env, kClassPathName, &gInterfaceObject);
    bail:
    return result;
}

void initClassHelper(JNIEnv *env, const char *path, jobject *objptr)
{
    jclass cls = (*env)->FindClass(env, path);
    if(!cls) {
        return;
    }
    jmethodID constr = (*env)->GetMethodID(env, cls, "<init>", "()V");
    if(!constr) {
        return;
    }
    jobject obj = (*env)->NewObject(env, cls, constr);
    if(!obj) {
        return;
    }
    (*objptr) = (*env)->NewGlobalRef(env, obj);

}

int handleCmd(const char * cmd)
{
    int flag = -1;
    JNIEnv *jniEnv = NULL;
    jclass javaClass = NULL;
    jmethodID javaMethod = NULL;

    bool isAttached = false;
    if(g_jvm == NULL)
        return flag;

    int status = (g_jvm)->GetEnv((void **) &jniEnv, JNI_VERSION_1_6);
    if(status < 0)
    {
        status = g_jvm->AttachCurrentThread(&jniEnv, NULL);
        if(status < 0)
            return flag;
        isAttached = true;
    }

    if(jniEnv == NULL )
    {
        g_jvm->DetachCurrentThread();
        return flag;
    }

    javaClass = jniEnv->GetObjectClass(gInterfaceObject);
    if(javaClass == NULL)
    {
        if(isAttached)
            g_jvm->DetachCurrentThread();
        return flag;
    }

    javaMethod = jniEnv->GetStaticMethodID(javaClass, "handleCmd","(Ljava/lang/String;)I");
    if(javaMethod == NULL)
    {
        if(isAttached)
            g_jvm->DetachCurrentThread();
        return flag;
    }

    jstring jstrMSG = NULL;
    jstrMSG =jniEnv->NewStringUTF(pkgName);
    flag = jniEnv->CallStaticIntMethod(javaClass,javaMethod,jstrMSG);
    jniEnv->DeleteLocalRef(jstrMSG);

    if(isAttached)
        g_jvm->DetachCurrentThread();

    return flag;
}
...

猜你喜欢

转载自blog.csdn.net/johnWcheung/article/details/81383785