JNI 案例1-----java调用c方法

来源:互联网 发布:seo推广服务 编辑:程序博客网 时间:2024/06/03 20:34
  1. 操作环境win10
  2. ide :eclipse和visual Studio 2013

我并不是一个很厉害的程序员,但我知道每天都要进步一点点,所以大家私信我的时候别说什么大神之类的话了,我很喜欢编程 我只是把他当成了我的爱好,很感谢CSDN这个平台分享交流的机会,但CSDN手机版app 不知道为什么点击博客不是加载不出来就是代码显示有问题,一个国内最大交流平台能一个像样的app都做不出来?

    • 前言
    • JNI的基本介绍
    • 案例
      • 编写javanative方法
      • 生成对应c的头文件XXh
      • 创建对应C工程
      • C代码函数编写
      • 生成动态链接库
      • Java加载动态链接库
    • 关于env一级指针和二级指针

前言

我以前在阅读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 __cplusplusextern "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语言的吧,

  1. 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();}
2 0
原创粉丝点击