1、JNI概述
JNI 是 Java Native Interface 的缩写,中文译为“Java 本地调用”。通俗的说JNI是一种技术,通过这种技术我们可以做到以下两点:
 1)Java 程序中的函数可以调用 Native 语言写的函数,Native 一般指的是C/C++编写的函数;
 2)Native 中的函数也可以调用Java层的函数。
 
JNI使用步骤
1.加载jni库
   System.loadLibrary("media_jni");
   一般在static静态块代码中加载,media_jni对应的JNI层是libmedia_jni.so, 对应的Native层是libmedia.so.
 
2.注册jni函数
   一.静态注册
   根据函数名来找对应的JNI函数,需要java的工具程序javah参与,流程如下:

先编写java代码,然后编译生成.class文件

使用java的工具程序javah,如javah -o output packagename.classname 这样就会生成一个叫output的JNI层头文件(函数名有_转换后为_l)

在静态方法中native函数是如何找到的,过程如下:当java层调用native_init函数时,它会从对应的JNI库中寻找java_android_media_MediaScanner_native_linit函数,如果没有找到,就会报错,如果找到就会为native_init和java_android_media_MediaScanner_native_linit建立一个函数指针,以后再调用native时直接使用这个指针即可,这个工作是由虚拟机完成

缺点:每个class都需要使用javah生成一个头文件,并且生成的名字很长书写不便;初次调用时需要依据名字搜索对应的JNI层函数来建立关联关系,会影响运行效率

     二.动态注册
     使用一种数据结构JNINativeMethod来记录Java native函数和JNI函数的对应关系
`typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;`

JNI_OnLoad函数

当我们使用System.loadLibarary()方法加载so库的时候,Java虚拟机就会找到这个函数并调用该函数,因此可以在该函数中做一些初始化的动作,其实这个函数就是相当于Activity中的onCreate()方法。该函数前面有三个关键字,分别是JNIEXPORT、JNICALL和jint,其中JNIEXPORT和JNICALL是两个宏定义,用于指定该函数是JNI函数。jint是JNI定义的数据类型,因为Java层和C/C++的数据类型或者对象不能直接相互的引用或者使用,JNI层定义了自己的数据类型,用于衔接Java层和JNI层,至于这些数据类型我们在后面介绍。这里的jint对应Java的int数据类型,该函数返回的int表示当前使用的JNI的版本,其实类似于Android系统的API版本一样,不同的JNI版本中定义的一些不同的JNI函数。该函数会有两个参数,其中*jvm为Java虚拟机实例,JavaVM结构体定义了以下函数:

DestroyJavaVM
AttachCurrentThread
DetachCurrentThread
GetEnv

这里我们使用了GetEnv函数获取JNIEnv变量,上面的JNI_OnLoad函数中有如下代码:

JNIEnv *env;
if (jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { return -1;
}

这里调用了GetEnv函数获取JNIEnv结构体指针,其实JNIEnv结构体是指向一个函数表的,该函数表指向了对应的JNI函数,我们通过调用这些JNI函数实现JNI编程。

获取Java对象,完成动态注册

上面介绍了如何获取JNIEnv结构体指针,得到这个结构体指针后我们就可以调用JNIEnv中的RegisterNatives函数完成动态注册native方法了。该方法如下:

jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)

第一个参数是Java层对应包含native方法的对象(这里就是AndroidJni对象),通过调用JNIEnv对应的函数获取class对象(FindClass函数的参数为需要获取class对象的类描述符):

jclass clz = env->FindClass("com/github/songnick/jni/AndroidJni");

第二个参数是JNINativeMethod结构体指针,这里的JNINativeMethod结构体是描述Java层native方法的,它的定义如下:

typedef struct {
const char* name;//Java层native方法的名字
const char* signature;//Java层native方法的描述符
void* fnPtr;//对应JNI函数的指针
} JNINativeMethod;

第三个参数为注册native方法的数量。一般会动态注册多个native方法,首先会定义一个JNINativeMethod数组,然后将该数组指针作为RegisterNative函数的参数传入,所以这里定义了如下的JNINativeMethod数组:

JNINativeMethod nativeMethod[] = {{"dynamicLog", "()V", (void*)nativeDynamicLog}};

最后调用RegisterNative函数完成动态注册:

env->RegisterNatives(clz, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]));

JNIEnv结构体

上面提到JNIEnv这个结构体,它就老厉害了,指向一个函数表,该函数表指向一系列的JNI函数,我们通过调用这些JNI函数可以实现与Java层的交互,这里简单的看看几个定义的函数:

..........
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
jboolean GetBooleanField(jobject obj, jfieldID fieldID)
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
CallVoidMethod(jobject obj, jmethodID methodID, ...)
CallBooleanMethod(jobject obj, jmethodID methodID, ...)
..........

这里简单的看看上面的四个函数,GetFieldID()函数是获取Java对象中某个变量的ID,GetBooleanField()函数是根据变量的ID获取数据类型为Boolean的变量。GetMethodID()函数是获取Java对象中对应方法的ID,CallVoidMethod()根据methodID调用对应对象中的方法,并且该方法的返回值为Void类型。通过这些函数我们可以实现调用Java层的代码。

JNI数据类型

上面我们提到JNI定义了一些自己的数据类型。这些数据类型是衔接Java层和C/C++层的,如果有一个对象传递下来,那么对于C/C++来说是没办法识别这个对象的,同样的如果C/C++的指针对于Java层来说它也是没办法识别的,那么就需要JNI进行匹配,所以需要定义一些自己的数据类型。

1.原始数据类型

Java Type Native Typ Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void N/A

2.引用类型

前面我们在获取AndroidJni对象的使用通过定义jclass引用,然后调用FindClass函数获取了该对象,所以JNI也定义了一些引用类型以便JNI层调用,具体的引用类型如下:

jobject                     (all Java objects)
|
|-- jclass (java.lang.Class objects)
|-- jstring (java.lang.String objects)
|-- jarray (array)
| |--jobjectArray (object arrays)
| |--jbooleanArray (boolean arrays)
| |--jbyteArray (byte arrays)
| |--jcharArray (char arrays)
| |--jshortArray (short arrays)
| |--jintArray (int arrays)
| |--jlongArray (long arrays)
| |--jfloatArray (float arrays)
| |--jdoubleArray (double arrays)
|
|--jthrowable

3.方法和变量的ID
 当需要调用Java中的某个方法的时候我们首先要获取它的ID,根据ID调用JNI函数获取该方法,变量的获取过程也是同样的过程,这些ID的结构体定义如下:

struct _jfieldID;              /* opaque structure */
typedef struct _jfieldID *jfieldID; /* field IDs */ struct _jmethodID; /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */

描述符

1.类描述符
 前面为了获取Java的AndroidJni对象,是通过调用FindClass()函数获取的,该函数参数只有一个字符串参数,我们发现该字符串如下所示:

com/github/songnick/jni/AndroidJni

其实这个就是JNI定义了对类的描述符,它的规则就是将"com.github.songnick.jni.AndroidJni"中的“.”用“/”代替。

2.方法描述符
 前面我们动态注册native方法的时候结构体JNINativeMethod中含有方法描述符,就是确定native方法的参数和返回值,我们这里定义的dynamicLog()方法没有参数,返回值为空所以对应的描述符为:"()V",括号类为参数,V表示返回值为空。下面还是看看几个栗子吧:

Method Descriptor Java Language Type
"()Ljava/lang/String;" String f();
"(ILjava/lang/Class;)J" long f(int i, Class c);
"([B)V" String(byte[] bytes);

上面的栗子我们看到方法的返回类型和方法参数有引用类型以及boolean、int等基本数据类型,对于这些类型的描述符在下个部分介绍。这里数组的描述符以"["和对应的类型描述符来表述。对于二维数组以及三维数组则以"[["和"[[["表示:

Descriptor Java Langauage Type
"[[I" int[][]
"[[[D" double[][][]

3.数据类型描述符
 前面我们说了方法的描述符,那么针对boolean、int等数据类型描述符是怎样的呢,JNI对基本数据类型的描述符定义如下:

Field Desciptor Java Language Type
Z boolean
B byte
C char
S short
I int
J long
F floa
D double

对于引用类型描述符是以"L"开头";"结尾,示例如下所示:

Field Desciptor Java Language Type
"Ljava/lang/String;" String
"[Ljava/lang/Object;" Object[]
 

JNI基础的更多相关文章

  1. [ 转载 ] Android JNI(一)——NDK与JNI基础

    Android JNI(一)——NDK与JNI基础 隔壁老李头 关注  4.4 2018.05.09 17:15* 字数 5481 阅读 11468评论 8喜欢 140 本系列文章如下: Androi ...

  2. Android JNI(一)——NDK与JNI基础

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  3. JNI基础概念以及原理-2016.01.11

    Java到C数据类型转换 1 基础类型 Java与Jni类型对应关系 2 String到char数组 具体使用方式 JNIEXPORT jstring JNICALL Java_com_zhoulee ...

  4. 【转】Android JNI编程—JNI基础

    原文网址:http://www.jianshu.com/p/aba734d5b5cd 最近看到了很多关于热补的开源项目——Depoxed(阿里).AnFix(阿里).DynamicAPK(携程)等,它 ...

  5. [Android Webkit]JNI基础及Java层与C++层的交互

    1. JNI 注册 1.1. JNI的基础结构       JAVA == JNI == Native Code      JNI(Java Native Interface)是Java与Native ...

  6. JNI基础学习

    1.JNI(Java Native Interface): 它允许Java代码和其他语言写的代码进行交互,JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要 ...

  7. JNI基础知识

    JNI是在学习Android HAL时必须要面临一个知识点,假设你不了解它的机制,不了解它的使用方式,你会被本地代码绕的晕头转向,JNI作为一个中间语言的翻译官在运行Java代码的Android中有着 ...

  8. JNI和NDK基础

    引言 JNI是Java Native Interface(Java本地接口),是为了方便Java调用C和C++等本地代码所封装的一层接口. NDK是Android提供的一个工具集合,通过NDK可以在A ...

  9. 【译文】JNI编程

    原文链接: https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html   没有逐字翻译,解说了文章的大 ...

随机推荐

  1. 洛谷 P3650 [USACO1.3]滑雪课程设计Ski Course Design

    P3650 [USACO1.3]滑雪课程设计Ski Course Design 题目描述 农民约翰的农场里有N座山峰(1<=N<=1000),每座山都有一个在0到100之间的整数的海拔高度 ...

  2. 2016最热门的PHP框架(一共五款)

    摘要: 兄弟连IT教育作为全国最大的PHP培训机构,迄今已有10年的教育历史.6大特色课程:PHP编程.安卓培训.JAVAEE+大数据.UI设计.HTML5培训.云计算架构师,在目前IT市场特别火,每 ...

  3. java程序猿经常使用的工具名称--知道中文意思吗

    在学习java的时候常常会碰到一些单词,可是一般的时候也不是非常在意这个单词的意思,而是能够了解到这个工具或者框架能够做什么就能够了.偶尔总结了一下还蛮有意思的.例如以下, 假设有遗漏,各位能够帮忙补 ...

  4. Android LoaderManager与CursorLoader用法

    一.基本概念 1.LoaderManager LoaderManager用来负责管理与Activity或者Fragment联系起来的一个或多个Loaders对象. 每个Activity或者Fragme ...

  5. 四种卸载Mac软件的方法

    从 Mac 电脑上卸载已经安装的应用程序可能是你知道的操作系统里面最简单的一种了.而如果你是一名新买了 Mac 电脑的用户,那么你可能比较困惑:怎么没有控制面板中的相应板块来卸载它们呢?但是其实你想不 ...

  6. HDU1392:Surround the Trees(凸包问题)

    Surround the Trees Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  7. storm集群操作指南

    目录 storm集群操作指南 一.storm伪分布式安装 (一)环境准备 (二)安装zookeeper (三)安装storm (四)运行程序 二.storm集群安装 (一)下载storm并解压 (二) ...

  8. Spark Streaming教程

      废话不说,先来个示例,有个感性认识再介绍. 这个示例来自spark自带的example,基本步骤如下: (1)使用以下命令输入流消息: $ nc -lk 9999 (2)在一个新的终端中运行Net ...

  9. 使用vue-cil搭建项目

    原文: 简书原文:https://www.jianshu.com/p/1af9b72cf86e 大纲 1.安装NodeJs 2.安装vue-cli 3.创建项目 4.启动项目 5.打包项目 6.项目实 ...

  10. win32中SetCapture 和 ReleaseCapture的使用(查一下在VCL中的使用)

    最近在用win32写<visual C++经典游戏程序设计>中的扫雷游戏,在写到鼠标点击雷区的时候用到了SetCapture,和ReleaseCapture这对系统函数. 那么为什么需要用 ...