赞
踩
对基本的JNI开发流程予以记录
参考书籍:《JNI_NDK开发指南》
参考博客:
JNI开发流程——东邪丶
JNI开发流程——Android百晓生
JNI全称为Java Native Interface,主要用于实现Java和C/C++的通信。
优势:
劣势:
public class JniTest { // 获取字符串 public static native String getStringFromC(); // 获取相加值 public static native int addFromC(int a, int b); static { // 需要在System.getProperty("java.library.path")路径下放入对应的jni_test.dll包 // System.loadLibrary("jni_test"); // 需要使用绝对路径,且需要添加后缀 System.load("C:/Users/kqli/IdeaProjects/shanguigu_interview_java/src/com/kqli/jni/jni_test.dll"); } public static void main(String[] args) { // 输出java library路径 // System.out.println(System.getProperty("java.library.path")); String stringFromC = getStringFromC(); System.out.println(); System.out.println(stringFromC); int a = 1, b = 1; System.out.println(String.format("%d + %d = %d", a, b, addFromC(a, b))); } }
Java加载native动态库,有两种API可实现:
①System.loadLibrary(“LibraryName”);
该API只需要指定动态库名字即可,不需要加前后缀。
且java会到java.library.path系统属性指定的目录下查找动态库文件,如果没有找到会抛出java.lang.UnsatisfiedLinkError异常。
若.dll文件有多个工程使用,可放到一个统一目录,在Path环境变量中配置存放路径,系统也能正确加载
②System.load(“/Users/Desktop/LibraryName.so”);
该API需要指定动态库的绝对路径名,且要加上前缀和后缀。
Java静态代码块中加载动态库,防止在未加载动态库之前就调用native方法
Java在创建类实例时,类会先被ClassLoader先加载到Java VM中,紧接着调用类的static静态代码块,所以在此时加载动态库可有效避免native方法调用比加载动态库时更早。
使用命令生成java工程.h文件
javac -h [目标文件路径] [文件名.java]
如出现编码格式异常则使用
javac -encoding utf-8 -h [目标文件路径] [文件名.java]
执行完成,会在src目录下生成.h头文件,如下图:
1)将.h头文件拷贝到c工程,并添加关联到项目(此时.h中#include<jni.h>头文件引入报错)
2)JDK中搜索jni.h,将jni.h和jni_md.h文件复制到C工程中,并修改引用为#include “jni.h”(解决jni.h头文件报错)
注:#include指令使用区别,系统头文件引入使用<>;第三方头文件引入使用" ",所以此处需修改引用为#include “jni.h”
3)创建c01.c、c02.c文件,实现C函数
Java调用C函数需要做C函数和Java本地方法的映射,建立该映射有两种方式: 显式映射和隐式映射。
在c01.c中实现Java与C函数隐式映射
#include "com_kqli_jni_JniTest.h"
#include "jni.h"
//函数实现
JNIEXPORT jstring JNICALL Java_com_kqli_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jclass) {
return (*env)->NewStringUTF(env, "Hello, kqli!");
}
JNIEXPORT jint JNICALL Java_com_kqli_jni_JniTest_addFromC(JNIEnv *env, jclass jclass, jint a, jint b)
{
return a + b;
}
在c02.c中实现Java与C函数显式映射
#include "jni.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) // C函数需要比Java本地方法多出两个参数,这两个参数之后的参数列表与Java本地方法保持一致 // 第一个参数表示JNI环境,该环境封装了所有JNI的操作函数 // 第二个参数为Java代码中调用该C函数的对象 // jint表示JNI的int类型,在本文后面会给出所有JNI类型 jint add(JNIEnv *env, jobject thiz, jint a, jint b) { return a + b; } jstring getString(JNIEnv *env, jobject thiz) { return (*env)->NewStringUTF(env, "hello, kqli!"); } static const JNINativeMethod methods[] = { // 第一个参数为Java本地方法名 // 第二个参数为函数签名:(参数签名)返回值签名, 在本文后面会给出所有签名符号 // 第三个参数为C函数 {"addFromC", "(II)I", (void *)add}, // 建立Java本地方法和C函数的映射 {"getStringFromC", "()Ljava/lang/String;", (void *)getString}, }; // 在Java中调用System.loadLibrary方法时会调用到该函数 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; jclass cls; // 获取JNI环境 if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_8)) { return JNI_ERR; } // 获取Java类 // JNI_OnLoad函数写法基本固定, 唯一需要修改的是FindClass的第二个参数,即类名 cls = (*env)->FindClass(env, "com/kqli/jni/JniTest"); if (cls == NULL) { return JNI_ERR; } // 注册本地方法 if ((*env)->RegisterNatives(env, cls, methods, ARRAY_SIZE(methods)) < 0) return JNI_ERR; return JNI_VERSION_10; }
动态库共有以下几种:
Windows编译.dll库
输出.o文件:gcc -c [.c文件名] -o [输出的.o文件]
输出.dll库:gcc [.o文件] -o [输出的.dll文件] -shared
将vs中生成的.dll动态库拷贝到java工程目录下,运行java工程
使用System.loadLibrary(“jni_test”)时,.dll文件须放到工程根目录下系统动态加载时才能找到,否则会报路径错误;
使用System.load(“C:/Users/kqli/IdeaProjects/shanguigu_interview_java/src/com/kqli/jni/jni_test.dll”)时,.dll文件必须与填写路径一致
运行成功!
待更新
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。