- 操作环境win10
- ide :eclipse和visual Studio 2013
我并不是一个很厉害的程序员,但我知道每天都要进步一点点,所以大家私信我的时候别说什么大神之类的话了,我很喜欢编程 我只是把他当成了我的爱好,很感谢CSDN这个平台分享交流的机会,但CSDN手机版app 不知道为什么点击博客不是加载不出来就是代码显示有问题,一个国内最大交流平台能一个像样的app都做不出来?
前言
我以前在阅读java源代码的时候点到某些方法带有native 然后具体实现没有
如Object类的 getClass方法
当我第一眼看到的时候 完全不知所以然,后来才知道native修饰方法是非本地java语言方法而是调用c语言.也就是我们常说的jni
JNI的基本介绍
JNI(Java Native Interface) java本地接口,用于c和java语言直接的相互调用.如上面的例子就是java调用c方法
案例
最好的老师就是案例
我们在eclipse创建一个工程 工程中只有一个文件DemoJni.ava(所在包为com.fmy)
如下图:
编写javanative方法
DeemoJni.java源码:
就只有一个 getCString方法
生成对应c的头文件(XX.h)
生成方法:
打开dos窗口
1. cd 你所在工程src目录下
2. javah 报名.类名
输入完成后会在src目录下生成对应的XXX.h文件
生成的文件命名也很有规则:包名类名方法名.h
如下:
我们打开文件看看:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_fmy_DemoJni */
#ifndef _Included_com_fmy_DemoJni
#define _Included_com_fmy_DemoJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_fmy_DemoJni
* Method: getCString
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_fmy_DemoJni_getCString
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
我们看下核心代码:
JNIEXPORT jstring JNICALL Java_com_fmy_DemoJni_getCString
(JNIEnv *, jobject, jstring);
这个看起来很眼熟吧?就是我们刚才java写的对应c方法函数声明,其命名规则如下 Java_包名_类名(JNIEnv *,jobject,参数…).
注意这里没有写参数变量名,到时候我们需要自己添加上
创建对应C工程
这里我们创建一个visual studio工程 把刚才刚才头文件放入工程中
然后在visutal studio添加
选中刚才添加工程目录的文件
然后打开文件:
可以看到红色波浪线报错.
原因是jni.h没有导入
jni.h位于java安装目录下,打开目录搜索即可看到
以下是我的目录
C:\Program Files\Java\jdk1.8.0_111\include
可以到jni.h然后像前面添加我们的javah生成的文件一样添加到工程(先放入工程,然后再visual studio添加)
然后打开com_fmy_Demo.h
#include<jni.h>改为#include"jni.h"
原因: `<>用于系统的头文件 ""用于系统和自定义的头文件`
然后我们打开jni.h文件看看
发现报错
原因:没有jni_md.h头文件
解决办法:jni_md.h位于 java安装目录下
以下是我的目录
C:\Program Files\Java\jdk1.8.0_111\include\win32
现在解决了所有问题 在看一下我们的工程目录
现在我们开始对应的c代码吧
C代码函数编写
我们在工程创建一个 01.c文件
先导入javah生成的头文件 本例”com_fmy_DemoJni.h”
1. 所以01.h源码
#include"com_fmy_DemoJni.h"
2. 复制com_fmy_DemoJni.h中函数声明到01.c文件中
可以看到编译报错
原因:参数没有写变量名
解决办法: 补全参数名(名字看你喜欢)
以下是个人命名:
其中:前面两个参数 是固定的 ,后面一个 jstring s是我们java声明的方法参数
我们回头看看java声明的:
String JString 参数对应 jstring s
我们继续看我们c语言的吧,
- JNIEnv *env 一个及其重要的参数 Env 是environment(环境)缩写,那么直接翻译就是 java本地接口环境,本质是一个结构体指针,在c环境是2级指针,在c++是一级指针(后面详解为什么有区别,先简单了解c++有this关键即可). env保存的结构体内含多种实现好的方法,和java和c语言基本数据类型转换和生成String字符串等.
然后继续写完代码吧
#include"com_fmy_DemoJni.h"
JNIEXPORT jstring JNICALL Java_com_fmy_DemoJni_getCString
(JNIEnv *env, jobject jobj, jstring s){
//用结构体的方法 生成对应java的字符串返回
return (*env)->NewStringUTF(env,"这个是来自C语言");
}
生成动态链接库
在windows中是dll文件, 在linux是so文件
我们来看看怎么做吧:
最后点击确认
然后在你项目的x64目录的Debug目录会有对应dll
Java加载动态链接库
看看运行结果:
乱码?
解决办法: 我们前面的01.c保存文件编码改为UTF-8即可
确定后保存文件!!!记得按下ctrl+s保存文件然后重新生成解决方案即可
运行结果:
关于env一级指针和二级指针
前面我们说env在c中是二级指针 ,在c++中env是二级指针.那么我们看看为什么吧.
假设Xiaoming去网吧打游戏对吧?然后万一突然电脑坏了那么需要换一台继续嗨i.
那么我以万物皆对象思想来分析到c中. 假设电脑是一个结构体computer.
Xiaoming是指针指向电脑内存
computer中有一个playgame需要传入Xiaoming指针的地址,和玩游戏两个参数方法,为什么要传入Xiaoming指针的地址?因为一旦playgame途中电脑坏了,那么需要重新换电脑对吧?那么就需要重新创建一个computer然后让Xiaoming重新指向另一个结构体 ,所以为了完成Xiaoming重新指向,所以注定了Xiaoming是一个二级指针,而C++有this关键字 所以不言而喻……
#include<stdio.h>
//网吧
struct cybercafe
{
void(*play_game)(struct MyStruct **a,char * game_name);
};
//用于实现cybercafe的play_game 函数,如果玩到一半电脑坏了那么需要直接更换电脑
//为了防止电脑坏了 换机的可能 需要传入指针地址,让其指针重新指向另一个电脑,
//那么我们就需要二级指针保存这个原来指针
void play_game(struct MyStruct **a, char * game_name){
//玩游戏中
//XXXXXX
//XXXXXX
//XXXXXX
//电脑蓝屏奔溃
//fuck 0x66666666
//fuck 0x66666666
//开启一个新电脑电脑
struct cybercafe computer2;
//换机
*a = &computer2;
}
void main(){
//假设我们创建 一个网吧类型cybercafe 内部只有一个paly_game函数 ,当我们玩游戏到一半电脑坏了 那么我就需要换电脑吧?
//这里就像当于这个结构体坏了 ,要出一个新的结构体 ,让小明这个指针重新指向另一个电脑
//xiaoming开启一个电脑 上网撸管
struct cybercafe computer1 = { play_game };
//xiaoming 输入上网账号到这个computer1上网
struct cybercafe *xiaoming = &computer1;
//小明开始玩游戏 大家觉不觉得写一个取地址很难受?写到这里我想大家应该想知道我想表达什么了
xiaoming->play_game(&xiaoming,"LOL英雄联盟");
getchar();
}