[RK3399][Android7.1] Andorid JNI知识点小结

JNI是什么?

JNI是Java Native Interface的缩写(Java本地调用)。
Java平台和本地C代码进行相互操作的API接口称为Java本地代码接口。

[RK3399][Android7.1] Andorid JNI知识点小结_第1张图片


什么时候该使用JNI?

Java之所以不能和C/C++想通,最主要的原因就是类型差异。那么该什么场景使用JNI?
1.当应用程序需要访问系统的各个特性和设备,并且这些特性和设备通过Java平台是无法访问时。
2.在一些要求效率和速度的场合,本地代码的运行效率高,比如复杂数学运算,游戏上的3D效果等等。 需要Native语言实现时。
3.当某个模块功能,已经有了一个成熟的,且经过大量测试的N ative代码实现,并且知道如何将其移植到所使用的目标平台上时。


JNI注册:

注册之意就是将Java层的native函数与JNI层对应的实现函数关联起来,这样在调用java层的native函数时,就能顺利转到JNI层对应的函数执行。

注册分静态注册和动态注册两种方式。

静态注册:

所谓的静态注册,就是直接在Java文件调用native库以及写个native方法 然后再c/c++文件中实现这个方法就行了!如:

test.java
static {
    System.loadLibrary("droidrobot");
}
public static native int getapkversion(String packagename);

test.cpp
jint Java_com_lib_droidrobot_getapkversion(JNIEnv *env,jobject thiz, jstring packagename)
{
    .....
    return 1;
}

动态注册:

动态注册好处是不要写很长的方法名

什么时候被调用注册?
Java层调用System.loadLibrary加载完JNI库后,紧接着会查找叫jni_onload的函数,如果有就调用它,而动态注册工作就在这里完成。
所以要实现动态注册,必须实现重写jni_onload方法。虽然静态方法没这个要求,但也建议实现一些,里面可以做一些初始化工作。
例子:

jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
    ......
    if (NULL != env && registerNativeMethods(env) == 0) {
        result = JNI_VERSION_1_4;
    }
        return result; //必须返回此值,否则报错
}
*registerNativeMethods:*
static int registerNativeMethods(JNIEnv* env) {
    //找到对应的类
    jclass clazz = env->FindClass("android/drm/DrmManagerClient");
    //把类和native的方法结合关联起来,所有方法都在nativeMethods数组中定义了。
    if (env->RegisterNatives(clazz, nativeMethods, sizeof(nativeMethods)![这里写图片描述](https://img-blog.csdn.net/20180413095539935?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2tyaXNfZmVp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
            / sizeof(nativeMethods[0])) == JNI_OK) {
        result = 0;
    }
    return result;
}

[RK3399][Android7.1] Andorid JNI知识点小结_第2张图片


Java层如何调用JNI?

Java调用native函数,就需要通过一个位于JNI层的动态库来实现,这个通常是在类的static语句中加载,调用System.loadLibrary方法,该方法的参数是动态库的名称。
如:

static {
    System.loadLibrary("droidrobot");
}

JNI如何调用JAVA层?

先看两个基本概念:

JNIEnv:

JNIEnv代表Java环境。通过这个JNIEnv*指针,就可以对Java端的代码进行操作。如创建Java类对象,调用Java对象的方法,获取Java对象的属性等。
JNIEnv是一个指向了一个Pointer,这个Pointer指向了一张函数表,这张函数表中的每一项就是JNI中的一个函数的入口。本地的方法通过查找这张表来调用某个jni函数,来和JVM交互。

[RK3399][Android7.1] Andorid JNI知识点小结_第3张图片

JNIEnv 与 JavaVM :
– JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个;
– JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个 JNIEnv;

JNIEnv 作用 :
– 调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码;
– 操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象;

书摘:
[RK3399][Android7.1] Andorid JNI知识点小结_第4张图片

[RK3399][Android7.1] Andorid JNI知识点小结_第5张图片

jclass:

为了能够在C/C++使用Java类,jni.h头文件中专门定义了jclass类型来表示Java中的Class类
jclass获取:
JNIEnv类中有如下几个简单的函数可以取得jclass

jclass FindClass(const char* clsName)  根据类名来查找一个类,完整类名。
jclass GetObjectClass(jobject obj)   根据一个对象,获取该对象的类
jclass GetSuperClass(jclass obj)     获取一个类的父类

FindClass 会在classpath系统环境变量下寻找类,需要传入完整的类名,注意包与包之间是用”/”而不是”.”来分割
如:

jclass cls_string= env->FindClass("java/lang/String");

jclass作用:
因为JNI中使用的方法名并非全路径名,所以要指明是哪个类。
比如调用类的静态方法,静态属性就需要通过这个方法来获取一个类。

访问Java层:

有了类和对象之后,用以下方法访问:
JNI在jni.h头文件中定义了jfieldID,jmethodID类表示Java端的属性和方法

  • 如何获取属性:
    在访问或设置Java属性的时候,首先就要现在本地代码中取得代表Java属性的jfieldID,然后才能在本地代码中进行Java属性操作。
  • 如何调用java的方法:
    调用Java端的方法时,需要取得代表方法的jmethodID才能进行Java方法调用
    JNIEnv获取fieldID和jmethodID的方法:
  1. GetFieldID/GetMethodID  //获取属性
  2. GetStaticFieldID/GetStaticMethodID  //获取方法

JNIEnv调用Java方法:

 1. (*jniEnv)->CallStaticObjectMethod(jniEnv, TestProvider, getTime);   //静态调用
 2. (*jniEnv)->CallVoidMethod(jniEnv, mTestProvider, sayHello,jstrMSG); //非静态调用

参考:

AndroidJNI 通过C++调用JAVA
JNI静态注册之Java调C一
android NDK开发 静态/动态注册 jni
JNI学习之步步深入一
深入理解JNI
Android JNI入门第六篇——C调用Java
《深入理解Android:卷一》

你可能感兴趣的:(RK3399,子类__Android)