Android JNI 深入理解JNINativeInterface函数<一>

Version Information(版本信息)

GetVersion

jint GetVersion(JNIEnv *env);

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

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 

SINCE JDK/JRE 1.4:

 #define JNI_VERSION_1_4 0x00010004

SINCE JDK/JRE 1.6:

#define JNI_VERSION_1_6 0x00010006
代码实现:

VersionInformation.cpp

extern "C"
JNIEXPORT jint JNICALL
Java_com_anniljing_jnidemo_VersionInformation_VersionInformation_getVersionInformation(JNIEnv *env,
                                                                                       jclass clazz) {
    return env->GetVersion();
}

VersionInformation.java

public class VersionInformation {
    public static native int getVersionInformation();
}

MainActivity.java

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

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

Class Operations(类操作)

DefineClass

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

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);

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();
}

jniDynamicLoadNative.cpp

//动态注册本地方法,需要c/c++中定义与native方法映射的方法
#include <jni.h>
#include <AndroidLog.h>

#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;
}

MainActivity

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


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+'\'' +
                '}';
    }
}

JniOperatorJavaClass.java

package com.anniljing.jnidemo.JavaClassOperator;

import com.anniljing.jnidemo.JavaClass;

public class JniOperatorJavaClass {
   public static native JavaClass operatorJavaClass();
}

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,"<init>", "()V");
    jobject  javaClassObg=env->NewObject(jc,construct);
    return javaClassObg;
}


在这里插入图片描述
PS:如遇到这样的提示,不用理会,继续码代码,能正常运行的

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值