一、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();
}