九、Android JNI 知识简介

在这里插入图片描述
在这里插入图片描述

前言:

硬件的底层驱动使用linux-c写的,我们可以用C编写简单的应用程序,直接调用open read write等c库函数,我们后边的android使用java写的,那么java怎么调用c的内容呢,就是通过JNI接口
JNI - Java Native Interface
在这里插入图片描述

一、Java访问C库的方法

1. 加载c库

System.loadLibrary();

2. 找到函数

建立java 函数到C函数的映射关系,在执行java某函数时,自动调用相应的C函数

2.1. 隐式建立映射关系
  • 类a.b.c.d.JNIDemo要调用hello函数
  • C语言中要实现Java_a_b_c_d_JNIDemo_hello
  • 可以用工具生成头文件
    • javac -d . JNIDemo.java
    • javah -jni a.b.c.d.JNIDemo
2.2. 显式建立映射关系

只要Java程序中加载了so库,首先要执行JNI_OnLoad() 这个方法
JNIDemo.java

public class JNIDemo{
	static {                               //静态代码块,实例化对象时,只调用一次
		/* 1. 加载c库 */
		System.loadLibrary("native");      // linux下生成 libnative.so

	}
	public native static void hello();    // native 表示本地函数,c/c++中实现的
	public static void main(String ares[]){
		
		/* 3. 调用   */
		hello();
	}
}

native.c

#include <stdio.h>
#include <jni.h>

#if 0
typedef struct {

    char *name;          /* Java里调用的函数名 */

    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */

    void *fnPtr;          /* C语言实现的本地函数 */

} JNINativeMethod;
#endif

void c_hello()  //本地方法 native_hello
{
	printf("hello world\n");
}


static const JNINativeMethod methods[] = {  //注册多个本地函数,对应不同的名字

	{"hello", "()V", (void *)c_hello},

};



/* 2. map java hello<---------->c c_hello */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "JNIDemo");  //查找c_hello 对应的 java hello所在的类,表示c_hello映射到这个类的hello
	if (cls == NULL) {
		return JNI_ERR;
	}
	
	if((*env)->RegisterNatives(env,cls,methods,1))   //运行环境env提供这个注册方法,这个方法注册到env环境的cls类中,
		return JNI_ERR;

	return JNI_VERSION_1_4;   //返回jni版本 有1.1 1.2 1.3 1.4
}

  • jni.h 的路径是
    在这里插入图片描述
  • 指定so库的路径
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述
  • 代码分析:

  • java中加载so库,就会立即调用JNI_OnLoad()这个函数
  • 在JNI_OnLoad() 中,首先获取env环境
  • 然后查找c_hello 对应的 java hello所在的类,表示c_hello映射到这个类的hello,生成类cls
  • 运行环境env提供这个注册方法RegisterNatives,这个方法注册到env环境的cls类中,在这个方法中实现c_hello 和java hello的映射
  • RegisterNatives中建立映射,需要定义RegisterNatives结构体类型的数组
- typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;         /* C语言实现的本地函数 */
} JNINativeMethod;
  • 重点是JNI字段描述符
    在这里插入图片描述
  • JNI字段描述符 ,也可以通过生成头文件获取
    在这里插入图片描述
  • 注意:C函数比Java里的声明多2个参数: (JNIEnv *env, jclass cls)
  • 在这里插入图片描述

二、Java和c库传递数据

2.1 传递基本类型数据

直接使用、直接返回,是透明的
举例:
JNIDemo.java

public class JNIDemo{
	static {                               //静态代码块,实例化对象时,只调用一次
		/* 1. 加载c库 */
		System.loadLibrary("native");      // linux下生成 libnative.so

	}
	public native static int hello(int a);    // native 表示本地函数,c/c++中实现的
	public static void main(String ares[]){
		
		/* 3. 调用   */
		System.out.println("java hello "+ hello(100));
	}
}

native.c

#include <stdio.h>
#include <jni.h>

#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif

jint c_hello(JNIEnv *env, jclass cls, jint a)  //本地方法 native_hello
{
	printf("hello world a = %d\n",a);
	return a+1;
}
static const JNINativeMethod methods[] = {  //注册多个本地函数,对应不同的名字
	{"hello", "(I)I", (void *)c_hello},
};

/* 2. map java hello<---------->c c_hello */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "JNIDemo");  //查找c_hello 对应的 java hello所在的类,表示c_hello映射到这个类的hello
	if (cls == NULL) {
		return JNI_ERR;
	}
	
	if((*env)->RegisterNatives(env,cls,methods,1))   //运行环境env提供这个注册方法,这个方法注册到env环境的cls类中,
		return JNI_ERR;

	return JNI_VERSION_1_4;   //返回jni版本 有1.1 1.2 1.3 1.4
}

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

2.2 传递字符串

举例:实现java传进去hello 返回world

JNIDemo.java

public class JNIDemo{
	static {                               //静态代码块,实例化对象时,只调用一次
		/* 1. 加载c库 */
		System.loadLibrary("native");      // linux下生成 libnative.so

	}
	public native static String hello(String str);    // native 表示本地函数,c/c++中实现的
	public static void main(String ares[]){
		
		/* 3. 调用   */
		System.out.println(hello("hello"));
	}
}
  • 运行java程序,参考生成的jni头文件,修改jni文件源码中JIN描述符和c本地函数声明
    在这里插入图片描述
  • 参考jni.pdf P39 ,字符串操作需要env提供的辅助函数,不能直接用,
    在这里插入图片描述
  • native.c
#include <stdio.h>
#include <jni.h>

#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif
jstring JNICALL c_hello(JNIEnv *env, jobject obj, jstring prompt)
{
	const jbyte *str;
	str = (*env)->GetStringUTFChars(env, prompt, NULL);   //通过这个函数,将传进来的prompt转化为str
	if (str == NULL) {
	return NULL; /* OutOfMemoryError already thrown */
	}
	printf("%s\n", str);

	(*env)->ReleaseStringUTFChars(env, prompt, str);     //把分配的空间释放掉
	return (*env)->NewStringUTF(env, "world");;
}

static const JNINativeMethod methods[] = {  //注册多个本地函数,对应不同的名字

	{"hello", "(Ljava/lang/String;)Ljava/lang/String;", (void *)c_hello},

};



/* 2. map java hello<---------->c c_hello */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "JNIDemo");  //查找c_hello 对应的 java hello所在的类,表示c_hello映射到这个类的hello
	if (cls == NULL) {
		return JNI_ERR;
	}
	
	if((*env)->RegisterNatives(env,cls,methods,1))   //运行环境env提供这个注册方法,这个方法注册到env环境的cls类中,
		return JNI_ERR;

	return JNI_VERSION_1_4;   //返回jni版本 有1.1 1.2 1.3 1.4
}
  • 运行结果
    在这里插入图片描述

2.3 传递数组

  • 举例:实现java传进去int 数组 返回和
  • JNIDemo.java
public class JNIDemo{
	static {                               //静态代码块,实例化对象时,只调用一次
		/* 1. 加载c库 */
		System.loadLibrary("native");      // linux下生成 libnative.so

	}
	public native static int hello(int[] a);    // native 表示本地函数,c/c++中实现的
	public static void main(String ares[]){

		int[] arr = {1,2,3};
		/* 3. 调用   */
		System.out.println(hello(arr));
	}
}
  • 运行java程序,参考生成的jni头文件,修改jni文件源码中JIN描述符和c本地函数声明
    在这里插入图片描述
  • 参考jni.pdf P48 ,数组操作需要env提供的辅助函数,不能直接用,
    在这里插入图片描述
    在这里插入图片描述
  • native.c
#include <stdio.h>
#include <jni.h>

#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif
JNIEXPORT jint JNICALL c_hello(JNIEnv *env, jobject obj, jintArray arr)
{
	jint *carr;
	jint i, sum = 0;
	carr = (*env)->GetIntArrayElements(env, arr, NULL);
	if (carr == NULL) {
	return 0; /* exception occurred */
	}
	for (i=0; i<(*env)->GetArrayLength(env,arr); i++) {
	sum += carr[i];
	}
	(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
	return sum;

}

static const JNINativeMethod methods[] = {  //注册多个本地函数,对应不同的名字

	{"hello", "([I)I", (void *)c_hello},

};



/* 2. map java hello<---------->c c_hello */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "JNIDemo");  //查找c_hello 对应的 java hello所在的类,表示c_hello映射到这个类的hello
	if (cls == NULL) {
		return JNI_ERR;
	}
	
	if((*env)->RegisterNatives(env,cls,methods,1))   //运行环境env提供这个注册方法,这个方法注册到env环境的cls类中,
		return JNI_ERR;

	return JNI_VERSION_1_4;   //返回jni版本 有1.1 1.2 1.3 1.4
}
  • 运行结果
    在这里插入图片描述

  • 举例:实现java传进去int 数组 返回数组,打印每个元素

  • JNIDemo.java

public class JNIDemo{
	static {                               //静态代码块,实例化对象时,只调用一次
		/* 1. 加载c库 */
		System.loadLibrary("native");      // linux下生成 libnative.so

	}
	public native static int[] hello(int[] a);    // native 表示本地函数,c/c++中实现的
	public static void main(String ares[]){

		int[] arr = {1,2,3};
		int[] rev = null;
		int i;
		/* 3. 调用   */
		rev = hello(arr);	
		for(i=0;i<rev.length;i++)
			System.out.println(rev[i]);
	}
}

native.c

#include <stdio.h>
#include <jni.h>
#include <stdlib.h>

#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif
JNIEXPORT jintArray JNICALL c_hello(JNIEnv *env, jobject obj, jintArray arr)
{
	jint *carr;
	jint *oarr;
	jint i,n;
	jintArray rarr;
	carr = (*env)->GetIntArrayElements(env, arr, NULL);
	if (carr == NULL) {
	return 0; /* exception occurred */
	}

	n = (*env)->GetArrayLength(env,arr);
	oarr = malloc(sizeof(jint)*n);
	if(oarr == NULL)
	{
		(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
		return 0;

	}

	for(i = 0;i<n;i++)
	{
		oarr[i] = carr[n-1-i];
	}
	(*env)->ReleaseIntArrayElements(env, arr, carr, 0);

	rarr = (*env)->NewIntArray(env,n);
	if(rarr == NULL)
		return 0;

	(*env)->SetIntArrayRegion(env,rarr,0,n,oarr);
	return rarr;

}

static const JNINativeMethod methods[] = {  //注册多个本地函数,对应不同的名字

	{"hello", "([I)[I", (void *)c_hello},

};



/* 2. map java hello<---------->c c_hello */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
	JNIEnv *env;
	jclass cls;
	if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
		return JNI_ERR; /* JNI version not supported */
	}
	cls = (*env)->FindClass(env, "JNIDemo");  //查找c_hello 对应的 java hello所在的类,表示c_hello映射到这个类的hello
	if (cls == NULL) {
		return JNI_ERR;
	}
	
	if((*env)->RegisterNatives(env,cls,methods,1))   //运行环境env提供这个注册方法,这个方法注册到env环境的cls类中,
		return JNI_ERR;

	return JNI_VERSION_1_4;   //返回jni版本 有1.1 1.2 1.3 1.4
}



  • 运行结果
    在这里插入图片描述
发布了29 篇原创文章 · 获赞 9 · 访问量 1876

猜你喜欢

转载自blog.csdn.net/m0_46291920/article/details/104532840