VC++ 6.0 JNI的实现

来源:互联网 发布:淘宝上的祛痘产品知乎 编辑:程序博客网 时间:2024/05/15 23:45

说明

本人从事Android开发工作,目前在研究JNI相关知识,关于JNI的学习,个人认为应该先从JNI本身入手,然后逐步扩展到Android方向,而不是直接开始Android方向JNI的开发和学习。为什么使用VC++ 6.0 ,因为大多数人大学学习C/C++的时候使用过这个工具,小巧,高效,虽然目前使用它开发时不现实的,但是用来学习还是很不错的。

JNI编写流程

  1. 编写带有native声明的方法的java类

  2. 使用javac命令编译所编写的java类

  3. 然后使用javah + java类名生成扩展名为h的头文件

  4. 使用C/C++实现本地方法

  5. 将C/C++编写的文件生成动态连接库

  6. 运行java工程并调用JNI

编写带有native声明的方法的java类

使用之前需要注意的是,一定要用32位的JDK,为什么?因为我们要利用vc++6.0生成dll库文件,但是vc++6.0是不支持生成64位dll的,所以JDK需要使用32位,不明白没关系,我们待会详细说。

HelloJni.java

    public class HelloJni{        static{            System.loadLibrary("HelloJni");        }        public native static void hello(String s);        public native static String say();        public static void main(String[] args){            HelloJni hello = new HelloJni();            hello.hello("Hello JNI !");            System.out.println(hello.say());        }    }

使用javac命令编译所编写的java类

进入java文件目录,生成class文件。

javac HelloJni.java

然后使用javah + java类名生成扩展名为h的头文件

javah HelloJni

可以看到该目录下生成了一个HelloJni.h的文件,主要内容是

    /* DO NOT EDIT THIS FILE - it is machine generated */    #include <jni.h>    /* Header for class HelloJni */    #ifndef _Included_HelloJni    #define _Included_HelloJni    #ifdef __cplusplus    extern "C" {    #endif    /*     * Class:     HelloJni     * Method:    hello     * Signature: (Ljava/lang/String;)V     */    JNIEXPORT void JNICALL Java_HelloJni_hello      (JNIEnv *, jclass, jstring);    /*     * Class:     HelloJni     * Method:    say     * Signature: ()Ljava/lang/String;     */    JNIEXPORT jstring JNICALL Java_HelloJni_say      (JNIEnv *, jclass);    #ifdef __cplusplus    }    #endif    #endif

该文件里面包含了需要实现的本地方法申明。

对于已生成的.h 头文件, C/C++所需要做的,就是把它的各个方法具体的实
现。然后编译连接成库文件,如果是在Linux环境下面就是Android里面常见的so库,而Windows下面则是dll文件。

在具体实现的时候,我们只关心两个函数原型

    NIEXPORT void JNICALL Java_HelloJni_hello(JNIEnv *, jclass, jstring);    NIEXPORT void JNICALL Java_HelloJni_hello(JNIEnv *, jclass, jstring);

这里 JNIEXPORT 和 JNICALL 都是 JNI 的关键字,表示此函数是要被 JNI 调用的。而 jstring 是JNI中定义的一种数据类型,目的是使得java的String类型和本地代表字符序列(c/c++中的字符数组)能够相互转化,我们可以把它当作一种中间类型,jstring暂且可以当做 String 来看待。可以看到函数的名称是 Java_再加上 java 类的 package路径再加函数名组成的。JNIEnv*和 jclass 我们暂时可以忽略。

使用C/C++实现本地方法

知道我们要实现的本地方法之后,我们可以用c++来实现这两个方法,并编译链接成dll文件。
我们新建一个HelloJni.cpp文件,将要实现的方法拷贝进去,并将HelloJni.c包含进去。

    #include "HelloJni.h"    const char* s;    JNIEXPORT void JNICALL Java_HelloJni_hello(JNIEnv * env, jclass cls, jstring str)    {        s = env->GetStringUTFChars(str, 0);     }    JNIEXPORT jstring JNICALL Java_HelloJni_say(JNIEnv * env, jclass cls)    {        return env->NewStringUTF(s);      }

将C/C++编写的文件生成动态连接库

实现好本地方法之后,接下来就是编译并链接成dll文件了。

打开vc6.0,我们新建一个Win32 Dynamic-Link Library 工程,并勾选一个简单的DLL工程
我们可以看到该工程里面已经有一个HelloJni.cpp文件了.

    #include "stdafx.h"    BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call,LPVOID lpReserved)    {        return TRUE;    }

注意#include "stdafx.h"待会会用到,接下来,这时候我们将之前生成的HelloJni.c和创建的HelloJni.cpp复制并覆盖到工程目录下,将#include "stdafx.h"添加到我们实现的HelloJni.cpp里面去,那么工程就变成这样。

    #include "stdafx.h"    #include "HelloJni.h"    const char* s;    JNIEXPORT void JNICALL Java_HelloJni_hello(JNIEnv * env, jclass cls, jstring str)    {        s = env->GetStringUTFChars(str, 0);     }    JNIEXPORT jstring JNICALL Java_HelloJni_say(JNIEnv * env, jclass cls)    {        return env->NewStringUTF(s);      }

点击Build之后编译没有问题进入到HelloJni工程目录下面的Debug目录下面就可以找到HelloJni.dll文件。那么dll文件就生成好了。

如果点击Compile之后报错,没有关系,先点击上面的Build之后再点Compile就会发现没有错误了。

运行java工程并调用JNI

拿到dll文件之后,将dll拷贝到HelloJni.class目录下面,运行java程序。

java HelloJni

输出

Hello JNI !

如果你是用的64位JDK的话,那么就会报错。

Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\johnny.wu\Desktop\HelloJni.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

所以这里需要32位的JDK,如果必须用64位的JDK那么就只能使用比VC6.0更强大的编译器生成64位的dll文件。

0 0
原创粉丝点击