一、JNI简介
JNI(Java Native Interface),即Java本地接口,是为java编写本地方法和jvm嵌入本地应用程序的标准的应用程序接口。首要的目标是在给定的平台上采用java通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在windows平台上dll文件形式,在UNIX机器上是so文件形式)。通过调用本地的库文件的内部方法,使java可以实现和本地机器的紧密联系,调用系统级的各接口方法。有的jvm来实现兼容的二进制编码本地方法库。
1、 建立java工程
在java程序中,首先需要在类中声明所调用的库名称:
//加载动态库
static{
System.loadLibrary("JniTest");
}
库的扩展名可以不用写出来,接着对将要调用的方法做本地声明,关键字native。
//静态方法
public native static String getStaticStringFromC();
//非静态方法
public native String getStringFromC(int i);
然后编译该java程序文件,生成class,在调用javah命令,jni就会生成C/C++的头文件。
建立java文件如下:
package com.zhufk;
public class JniTest {
//静态方法
public native static String getStaticStringFromC();
//非静态方法
public native String getStringFromC(int i);
public static void main(String[] args) {
String text = getStaticStringFromC();
System.out.println(text);
JniTest t=new JniTest();
String text2=t.getStringFromC(7);
System.out.println(text2);
}
//加载动态库
static{
System.loadLibrary("JniTest");
}
}
2、生成动态链接库
生成.h文件如下;
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_zhufk_JniTest */
#ifndef _Included_com_zhufk_JniTest
#define _Included_com_zhufk_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_zhufk_JniTest
* Method: getStaticStringFromC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zhufk_JniTest_getStaticStringFromC
(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_com_zhufk_JniTest_getStringFromC
(JNIEnv *, jobject,jint);
#ifdef __cplusplus
}
#endif
#endif
3、新建c工程
将生成的.h文件拷贝到工程头文件中,必须注意的是要将sdk中jni.h和jni_md.h文件到CPP工程中。
处理本地方法如下:
#include "com_zhufk_JniTest.h"
//函数实现
JNIEXPORT jstring JNICALL Java_com_zhufk_JniTest_getStaticStringFromC
(JNIEnv * env, jclass jcls){
return (*env)->NewStringUTF(env, "C String");
}
//函数实现
JNIEXPORT jstring JNICALL Java_com_zhufk_JniTest_getStringFromC
(JNIEnv * env, jobject jobj,jint num){
return (*env)->NewStringUTF(env, "C String2");
}
//每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)
//当native方法为静态方法时:
//jclass 代表native方法所属类的class对象(JniTest.class)
//当native方法为非静态方法时:
//jobject 代表native方法所属的对象
总结:
1.编写native方法
2.javah命令,生成.h头文件
3.复制.h头文件到CPP工程中
4.复制jni.h和jni_md.h文件到CPP工程中
5.实现.h头文件中声明的函数
6.生成dll文件
7.配置dll文件所在目录到环境变量
8.重启Eclipse
补充C语言预编译
C语言执行的流程
1、编译:形成目标代码(.obj)
2、连接:将目标代码与C函数库连接合并,形成最终的可执行文件
3、执行
预编译(预处理),为编译做准备工作,完成代码文本的替换工作 ,头文件告诉编译器有这样一个函数,连接器负责找到这个函数的实现
//宏定义、宏替换、预编译指令
//define指令
//1.定义标示
//#ifdef __cplusplus 标识支持C++语法//如果没有定义AH,定义AH
//#ifndef AH
//#define AH
//#include "B.h"
//#endi
//该头文件只被包含一次,让编译器自定处理好循环包含问题
#pragma once
#include "B.h"void printfA();
//防止文件重复引入
//2.定义常数(便于修改与阅读)#define MAX 100
//int MIN = 40;
//3.定义“宏函数”
void dn_com_jni_read(){
printf("read\n");
}
void dn_com_jni_write(){
printf("write\n");
}
//NAME是参数
#define jni(NAME) dn_com_jni_##NAME();
//webrtc JNI函数名称很长,也是JOW宏函数缩短函数名称
//日志输出
//__VA_ARGS__可变参数
//#define LOG(FORMAT,...) printf(##FORMAT,__VA_ARGS__);
日志会有级别
//#define LOG_I(FORMAT,...) printf("INFO:"); printf(##FORMAT,__VA_ARGS__);
//#define LOG_E(FORMAT,...) printf("ERRO:"); printf(##FORMAT,__VA_ARGS__);
//升级版本
#define LOG(LEVEL,FORMAT,...) printf(##LEVEL); printf(##FORMAT,__VA_ARGS__);
#define LOG_I(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS__);
#define LOG_E(FORMAT,...) LOG("ERROR:",##FORMAT,__VA_ARGS__);
#define LOG_W(FORMAT,...) LOG("WARN:",##FORMAT,__VA_ARGS__);
//Android
//#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
//LOGI("%s","fix");
//替换
//__android_log_print(ANDROID_LOG_INFO, "jason", "%s", "fix");
void main(){
//#include "my.txt"
//printf("%s\n", "I am a little boy!");
//printfA();
int i = 90;
if (i < MAX){
printf("比MAX小..");
}
jni(write);//替换:dn_com_jni_write();
LOG_E("%s%d","大小:",89);
//替换成:printf("INFO:"); printf("%s%d","大小:",89);
//LOG_I
getchar();
}