安卓驱动开发之-(一)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


【安卓驱动开发之-(一)JNI】
二、测试:
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

    推荐阅读