• 深入理解JNINativeInterface函数<一>


    Version Information(版本信息)

    GetVersion

    jint GetVersion(JNIEnv *env);
    
    • 1

    返回本地方法接口的版本。

    PARAMETERS(参数):
    env:JNI接口指针。

    RETURNS(返回值):
    返回高16位的主版本号和低16位的次版本号。

    在JDK/JRE 1.1中,GetVersion()返回0x00010001。

    在JDK/JRE 1.2中,GetVersion()返回0x00010002。

    在JDK/JRE 1.4中,GetVersion()返回0x00010004。

    在JDK/JRE 1.6中,GetVersion()返回0x00010006。

    常量
    SINCE JDK/JRE 1.2:

    #define JNI_VERSION_1_1 0x00010001
    #define JNI_VERSION_1_2 0x00010002
    
    /* Error codes */
    #define JNI_EDETACHED    (-2)              /* thread detached from the VM */
    #define JNI_EVERSION     (-3)              /* JNI version error 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    SINCE JDK/JRE 1.4:

     #define JNI_VERSION_1_4 0x00010004
    
    • 1

    SINCE JDK/JRE 1.6:

    #define JNI_VERSION_1_6 0x00010006
    
    • 1

    代码实现:

    VersionInformation.cpp

    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_anniljing_jnidemo_VersionInformation_VersionInformation_getVersionInformation(JNIEnv *env,
                                                                                           jclass clazz) {
        return env->GetVersion();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    VersionInformation.java

    public class VersionInformation {
        public static native int getVersionInformation();
    }
    
    • 1
    • 2
    • 3

    MainActivity.java

      public void toVersionInformation(View view) {
            int versionInformation = VersionInformation.getVersionInformation();
            Log.d(TAG, "VersionInformation:0x" + Integer.toHexString(versionInformation));
        }
    
    • 1
    • 2
    • 3
    • 4

    运行结果:
    在这里插入图片描述

    Class Operations(类操作)

    DefineClass

    jclass DefineClass(JNIEnv *env, const char *name, jobject loader,const jbyte *buf, jsize bufLen);
    
    • 1

    Loads a class from a buffer of raw class data. The buffer containing the raw class data is not referenced by the VM after the DefineClass call returns, and it may be discarded if desired.

    从原始类数据的缓冲区加载一个类。 在 DefineClass 调用返回后,VM 不会引用包含原始类数据的缓冲区,如果需要,它可能会被丢弃。
    PARAMETERS(参数):
    env: the JNI interface pointer.
    JNI接口指针

    name: the name of the class or interface to be defined. The string is encoded in modified UTF-8.
    要定义的类或接口的名称,该字符串使用修改后的UTF-8编码

    loader: a class loader assigned to the defined class.
    分配给已定义类的类加载器

    buf: buffer containing the .class file data.
    包含.class文件数据的缓冲区

    bufLen: buffer length.
    缓冲区长度

    RETURNS(返回值):
    Returns a Java class object or NULL if an error occurs.
    返回java类对象,或者出现错误的时候返回NULL

    THROWS(异常):
    ClassFormatError: if the class data does not specify a valid class.
    类格式化错误:如果不是有效的类,则抛出该错误

    ClassCircularityError: if a class or interface would be its own superclass or superinterface.
    类循环错误:如果该类或者接口的父类或者父接口为自己本身,则会抛出该错误

    OutOfMemoryError: if the system runs out of memory.
    内存溢出错误:如果系统运行出现内存溢出,则会抛出该错误

    SecurityException: if the caller attempts to define a class in the “java” package tree.
    安全异常:如果调用者企图在"java"源码包下定义一个类,则会抛出该异常

    FindClass

    jclass FindClass(JNIEnv *env, const char *name);
    
    • 1

    In JDK release 1.1, this function loads a locally-defined class. It searches the directories and zip files specified by the CLASSPATH environment variable for the class with the specified name.
    在JDK1.1,该方法用来加载本地定义的类。它会从CLASSPATH环境变量指向的文件夹和zip文件

    Since Java 2 SDK release 1.2, the Java security model allows non-system classes to load and call native methods. FindClass locates the class loader associated with the current native method; that is, the class loader of the class that declared the native method. If the native method belongs to a system class, no class loader will be involved. Otherwise, the proper class loader will be invoked to load and link the named class.
    从JDK 1.2发行版之后,Java安全模型允许非本地系统类也可以加载和调用native方法(native methods)。FindClass 函数会加载与当前native方法关联的类加载器(class loader), 也就是定义了native方法的类的类加载器(class loader)。如果该native方法属于系统类(system class),则没有ClassLoader会被调用。否则,将使用正确的ClassLoader来加载(load)和链接(link)指定名称的类。

    PS:我们需要重点理解这句话(FindClass locates the class loader associated with the current native method; that is, the class loader of the class that declared the native method)
    1、FindClass locates the class loader
    FindClass是加载类加载器的
    2、associated with the current native method
    与当前native方法相关联的
    3、that is,the class loader of the class that declared the native method
    也就是定义了native方法的类的类加载器

    Since Java 2 SDK release 1.2, when FindClass is called through the Invocation Interface, there is no current native method or its associated class loader. In that case, the result of ClassLoader.getSystemClassLoader is used. This is the class loader the virtual machine creates for applications, and is able to locate classes listed in the java.class.path property.
    从JDK 1.2发行版之后,当通过 Invocation 接口来调用 FindClass 函数,将没有当前的本地方法或与之关联的ClassLoader。这种情况下,会使用 ClassLoader.getSystemClassLoader 来替代。这个ClassLoader 是虚拟机用来创建应用(applications)的,它有能力定位到 java.class.path 参数下的所有类。

    PARAMETERS(参数):
    env: the JNI interface pointer.
    JNI接口指针

    name: a fully-qualified class name (that is, a package name, delimited by “/”, followed by the class name). If the name begins with “[“ (the array signature character), it returns an array class. The string is encoded in modified UTF-8
    全称的类名(包名以 / 作为分隔符, 然后紧跟着类名),如果名字以 [开头(数组签名标识符),则返回一个数组的类,这个字符串也是MUTF-8

    RETURNS(返回值):
    Returns a class object from a fully-qualified name, or NULL if the class cannot be found.
    指定名称的类的对象(a class object),或者没有找到对应的类时则返回 NULL

    THROWS(异常):
    ClassFormatError: if the class data does not specify a valid class.
    类格式化错误:如果不是有效的类,则抛出该错误

    ClassCircularityError: if a class or interface would be its own superclass or superinterface.
    类循环错误:如果该类或者接口的父类或者父接口为自己本身,则会抛出该错误

    OutOfMemoryError: if the system runs out of memory.
    内存溢出错误:如果系统运行出现内存溢出,则会抛出该错误

    SecurityException: if the caller attempts to define a class in the “java” package tree.
    安全异常:如果调用者企图在"java"源码包下定义一个类,则会抛出该异常

    代码实现

    1、动态注册native方法时

    JniDynamicLoad.java

    public class JniDynamicLoad {
        public native int sum(int x, int y);
    
        public native String getNativeString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    jniDynamicLoadNative.cpp

    //动态注册本地方法,需要c/c++中定义与native方法映射的方法
    #include 
    #include 
    
    #define JAVA_CLASS "com/anniljing/jnidemo/JniDynamicLoad"
    
    jint sum(JNIEnv *env, jobject jobj, int x, int y) {
        return x + y;
    }
    
    jstring getMessage(JNIEnv *env, jobject jobj) {
        return env->NewStringUTF("This is from native string");
    }
    
    static JNINativeMethod gMethods[] = {
            {"sum",             "(II)I",                (void *) sum},
            {"getNativeString", "()Ljava/lang/String;", (void *) getMessage}
    };
    
    int registerNativeMethods(JNIEnv *env, const char *name, const JNINativeMethod *methods,
                              jint nMethods) {
        jclass jcls;
        jcls = env->FindClass(name);
        if (jcls == nullptr) {
            return JNI_FALSE;
        }
        if (env->RegisterNatives(jcls, methods, nMethods) < 0) {
            return JNI_FALSE;
        }
        return JNI_TRUE;
    }
    
    #define NELEM(x) ((int)(sizeof(x)/sizeof((x)[0])))
    
    JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env;
        if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
            return JNI_FALSE;
        }
        registerNativeMethods(env, JAVA_CLASS, gMethods, NELEM(gMethods));
        LOGD("Dynamic load success");
        return JNI_VERSION_1_6;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    MainActivity

    public void findClass(View view) {
            JniDynamicLoad dynamicLoad = new JniDynamicLoad();
            Log.d(TAG, dynamicLoad.getNativeString());
        }
    
    • 1
    • 2
    • 3
    • 4


    2、创建java普通对象时
    JavaClass.java

    package com.anniljing.jnidemo;
    
    import android.util.Log;
    
    public class JavaClass {
        public static String name;
        private String version;
        private static int sCode;
    
        public JavaClass() {
            Log.d("MainActivity","Init JavaClass");
        }
    
        public String getVersion() {
            return version;
        }
    
        public void setVersion(String version) {
            this.version = version;
        }
    
        public static void setCode(int code){
            sCode=code;
        }
    
        @Override
        public String toString() {
            return "JavaClass{" +
                    "version='" + version + '\''+"Code="+sCode+'\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    JniOperatorJavaClass.java

    package com.anniljing.jnidemo.JavaClassOperator;
    
    import com.anniljing.jnidemo.JavaClass;
    
    public class JniOperatorJavaClass {
       public static native JavaClass operatorJavaClass();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    JavaClassOperator.cpp

    extern "C"
    JNIEXPORT jobject JNICALL
    Java_com_anniljing_jnidemo_JavaClassOperator_JniOperatorJavaClass_operatorJavaClass(JNIEnv *env,
                                                                                        jclass clazz) {
        jclass jc=env->FindClass("com/anniljing/jnidemo/JavaClass");
        //调用java类的构造方法
        jmethodID construct=env->GetMethodID(jc,"", "()V");
        jobject  javaClassObg=env->NewObject(jc,construct);
        return javaClassObg;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10


    参考官方文档地址
    代码已同步GitHub

  • 相关阅读:
    Net6使用Yarp做网关
    克隆自己的声音——赛博分身必备技能
    纯后端如何写前端?我用了低代码平台
    SLF4J:Failed to load class org.slf4j.impl.StaticLoggerBinder.
    C# this的五种用法
    lotus-local-net 8MiB v1.17.1-rc3 本地测试环境
    Python毕业设计源代码OA在线办公系统
    2022PMP项目管理认证考试报考指南(1)
    【AntDesign】Docker部署
    Redis高可用与持久化
  • 原文地址:https://blog.csdn.net/u011557841/article/details/126307155