安卓驱动开发之-(一)JNI

准备知识.

JNI概念:Java本地接口,Java Native Interface, 它是一个协议, 该协议用来沟通Java代码和外部的本地C/C++代码, 通过该协议 Java代码可以调用外部的本地代码, 外部的C/C++ 代码可以调用Java代码。

C与Java如何交流:

-- JNI规范 : C语言与Java语言交流需要一个适配器, 中间件, 即 JNI, JNI提供了一种规范;
-- C语言中调用Java方法 : 可以让我们在C代码中找到Java代码class中的方法, 并且调用该方法;
-- Java语言中调用C语言方法 : 同时也可以在Java代码中, 将一个C语言的方法映射到Java的某个方法上;
-- JNI桥梁作用 : JNI提供了一个桥梁, 打通了C语言和Java语言之间的障碍;

JNI中的一些概念 :
-- native : Java语言中修饰本地方法的修饰符, 被该修饰符修饰的方法没有方法体;
-- Native方法 : 在Java语言中被native关键字修饰的方法是Native方法;
-- JNI层 : Java声明Native方法的部分;
-- JNI函数 : JNIEnv提供的函数, 这些函数在jni.h中进行定义;
-- JNI方法 : Native方法对应的JNI层实现的 C/C++方法, 即在jni目录中实现的那些C语言代码;

Android程序框架:

正常情况下的Android框架 : 最顶层是Android的应用程序代码, 上层的应用层 和 应用框架层 主要是Java代码, 中间有一层的Framework框架层代码是 C/C++代码, 通过Framework进行系统调用, 调用底层的库 和linux 内核。

使用JNI时的Android框架 : 绕过Framework提供的调用底层的代码, 直接调用自己写的C代码, 该代码最终会编译成为一个库, 这个库通过JNI提供的一个Stable的ABI 调用linux kernel;ABI是二进制程序接口 application binary interface。

JNI在Android中作用 : 

JNI可以调用本地代码库(即C/C++代码), 并通过 Dalvik虚拟机 与应用层 和 应用框架层进行交互, Android中JNI代码主要位于应用层 和 应用框架层;
-- 应用层 : 该层是由JNI开发, 主要使用标准JNI编程模型;
-- 应用框架层 : 使用的是Android中自定义的一套JNI编程模型, 该自定义的JNI编程模型弥补了标准JNI编程模型的不足;

JNI编程步骤:
-- 声明native方法 : 在Java代码中声明 native method()方法;
-- 实现JNI的C/C++方法 : 在JNI层实现Java中声明的native方法, 这里使用javah工具生成带方法签名的头文件, 该JNI层的C/C++代码将被编译成动态库;
-- 加载动态库 : 在Java代码中的静态代码块中加载JNI编译后的动态共享库;


下面将一下介绍如何编写HAL层(硬件抽象层)对应的JNI方法

java如果想调用程序,是怎样的一个流程呢?

一、构造c函数

首先在构造一个java可以调用的一个class,这个类由这个结构体描述:

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

因此我们先注册构造一个methods方法:

static const JNINativeMethod methods[] = {
	{"hello", "([I)[I", (void *)c_hello},
};

第一个参数为注册的函数名称;第二个参数为函数的参数和返回值,具体的可参考JNI的手册,第三个参数为函数注册额函数。


下面我们实现一个c函数,传入的参数类型为一个数组,返回值为数组的倒序:

jintArray c_hello(JNIEnv *env, jobject cls, jintArray arr)
{
	jint *carr;
	jint *oarr;
	jintArray rarr;
	
	jint i, n = 0;
	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);

	/* create jintArray */
	rarr = (*env)->NewIntArray(env, n);
	if (rarr == NULL)
	{
		return 0;
	}

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

装载该c函数:

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");
	if (cls == NULL) {
		return JNI_ERR;
	}

	/* 2. map java hello <-->c c_hello */
	if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)
		return JNI_ERR;

	return JNI_VERSION_1_4;
}

补充一个该c函数所用到重要的头文件:

#include 

二、测试:
public class JNIDemo {
	static { 		/* 1. load */
		System.loadLibrary("native"); /* libnative.so */
 	}
	public native int[] hello(int[] a);
	public static void main (String args[]) {
		JNIDemo d = new JNIDemo();	
		int [] a = {1, 2, 3}; 
		int [] b = null;
		int i;

		/* 2. map java hello <-->c c_hello */

		/* 3. call */
		b = d.hello(a);

		for (i = 0; i < b.length; i++)		
			System.out.println(b[i]);
	}
}
结果输出:321

你可能感兴趣的:(安卓驱动开发)