• Java Native Interfce三在JNI中使用Java类的普通方法与变量


    本文是《The Java Native Interface Programmer’s Guide and Specification》读书笔记


    前面我们学习了如何在JNI中通过参数来使用Java中的普通变量,下面我们来学习如何在JNI中使用Java类的普通方法与成员变量;Java中类有两个区域:每个类的实例的私有区域和所有实例共享的类的静态区域,JNI也提供了相应的方法来操作这两个区域中的方法与变量。首先以一个简单例子来说明如何在JNI中操作类实例的私有区域:

    1 变量的操作

    1.1 类实例的私有区域(成员变量)操作:

    Java类的代码为:

    class InstanceFieldAccess {
        private String s;//类实例的私有区域,修饰符也可以为public
        //JNI方法,用来修改类实例的私有区域的变量
        private native void accessField();
        public static void main(String args[]) {
            InstanceFieldAccess c = new InstanceFieldAccess();
            c.s = "abc";
            c.accessField();
            System.out.println("In Java:");
            System.out.println("  c.s = "" + c.s + """);
        }
        static {
            System.loadLibrary("InstanceFieldAccess");
        }
    }
    

    JNI方法的实现为:

    JNIEXPORT void JNICALL
    Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj)
    {
        jfieldID fid;   /* 保存区域的标识符 */
        jstring jstr;
        const char *str;
        /* 得到obj对象的类的引用,这里是前面的类InstanceFieldAccess的引用 */
        jclass cls = (*env)->GetObjectClass(env, obj);
        printf("In C:
    ");
        /* 在类的私有区域里寻找名为s的区域标识 */
        fid = (*env)->GetFieldID(env, cls, "s",
                                 "Ljava/lang/String;");
        if (fid == NULL) {
            return; /* failed to find the field */
        }
            /* 取得相应区域标识的实例*/
        jstr = (*env)->GetObjectField(env, obj, fid);
        str = (*env)->GetStringUTFChars(env, jstr, NULL);
        if (str == NULL) {
            return; /* out of memory */
        }
        printf("  c.s = "%s"
    ", str);
        (*env)->ReleaseStringUTFChars(env, jstr, str);
        /* 创建一个新的String对象 */
        jstr = (*env)->NewStringUTF(env, "123");
        if (jstr == NULL) {
            return; /* out of memory */
        }
        /*将创建的String对象的值写入到相应区域标识里*/
        (*env)->SetObjectField(env, obj, fid, jstr);
    }
    

    执行完这个JNI方法后,s的值就被修改为"123"了;

    1.2 对类的静态区域的(类变量)操作

    Java类的定义为:

    public StaticFieldAccess{
      private static int si;
      private native void accessField();
      ...
    }
    

    对应的JNI的方法的实现为:

    JNIEXPORT void JNICALL
    Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj)
    {
        jfieldID fid;   /*用来保存区域的标识 */
        jint si;
        /* Get a reference to obj’s class */
        jclass cls = (*env)->GetObjectClass(env, obj);
        printf("In C:
    ");
        /* Look for the static field si in cls */
        fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
        if (fid == NULL) {
            return; /* field not found */
        }
        /* Access the static field si */
        si = (*env)->GetStaticIntField(env, cls, fid);
        printf("  StaticFieldAccess.si = %d
    ", si);
        (*env)->SetStaticIntField(env, cls, fid, 200);
    }
    

    由上可以看出,在JNI中操作类实例的私有区域与操作类的静态区域是类似的,在得到区域的标识时,使用不同的方法就可以了。前面的操作看起来比较复杂,是因为String不是基本的类型,需要一些特殊的处理,而int为基本类型,不需要进行特殊的处理;

    2. 普通方法的使用

    2.1 类的实例的成员方法的使用

    Java中类的定义如下:

    public class  InstanceMethodCall{
     private native void nativeMethod();
     private void callback(){
     System.out.println("In Java");
     }
     ...
    }
    

    JNI方法的实现

    JNIEXPORT void JNICALL
    Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj)
    {
        jclass cls = (*env)->GetObjectClass(env, obj);
        //得到对应方法的标识ID
        jmethodID mid =
            (*env)->GetMethodID(env, cls, "callback", "()V");
        if (mid == NULL) {
            return; /* method not found */
        }
        printf("In C
    ");
        //调用对应的成员方法,相应的还有CallIntMethod方法调用返回值为int的方法,CallObjectMethod方法调用返回值为对象的方法(数组,String等)等;
        (*env)->CallVoidMethod(env, obj, mid);
    }
    

    JNI方法调用类的普通方法的流程参考以上代码;

    2.2类方法的使用

    java类的定义如下:

    public class StaticMethodCall{
    private native void nativeMethod();
    private static void callback(){
    System.out.println("In java");
    }
    ...
    }
    

    对应的JNI方法的实现为:

    JNIEXPORT void JNICALL
    Java_StaticMethodCall_nativeMethod(JNIEnv *env, jobject obj)
    {
        jclass cls = (*env)->GetObjectClass(env, obj);
        jmethodID mid =
    (*env)->GetStaticMethodID(env, cls, "callback", "()V");
        if (mid == NULL) {
            return;  /* method not found */
        }
        printf("In C
    ");
        (*env)->CallStaticVoidMethod(env, cls , mid);
    }
    

    类方法与类的实例的成员方法的调用也类似,只不过在获得方法标识时,需要调用不同的方法;

    2.3 构造方法的调用

    构造方法的调用与实例的成员方法的调用类似,下面以字符串的构造函数String(char[]chars)的调用为例:

     jstring
    MyNewString(JNIEnv *env, jchar *chars, jint len)
    {
        jclass stringClass;
        jmethodID cid;
        jcharArray elemArr;
        jstring result;
        stringClass = (*env)->FindClass(env, "java/lang/String");
        if (stringClass == NULL) {
            return NULL; /* exception thrown */
    }
    /* 得到构造函数String(char[]) 的方法标识 */
        cid = (*env)->GetMethodID(env, stringClass,
                                  "<init>", "([C)V");
        if (cid == NULL) {
            return NULL; /* exception thrown */
        }
        /* 创建一个charArray来保存这些字符 */
        elemArr = (*env)->NewCharArray(env, len);
        if (elemArr == NULL) {
            return NULL; /* exception thrown */
        }
        (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);
        /* 构造一个 java.lang.String的对象,这就是调用构造函数的地方 */
    result = (*env)->NewObject(env, stringClass, cid, elemArr);
        /* 释放掉本地的引用 */
        (*env)->DeleteLocalRef(env, elemArr);
        (*env)->DeleteLocalRef(env, stringClass);
        return result;
    }
    
  • 相关阅读:
    Android 入门到精通 (Index)
    负载平衡与冗余备份方案概述
    Android 程序组件交互分析
    复制时保留文件的目录结构
    notepad++中设置tab缩进的宽度
    scws
    php 将字符(包括汉字) 转换成16进制 (apache access log 中文显示16进制码)
    批量修改完整版本
    根据端口号查进程
    php性能优化
  • 原文地址:https://www.cnblogs.com/WoodJim/p/4795281.html
Copyright © 2020-2023  润新知