• JNI技术详解,让程序有飞一般的感觉


    一.前言

    如果你对程序的性能要求比较高,或者觉得java的运行速度已经满足不了你,底层也可以采用C++来完成,使用JNI技术直接调用,会让你的程序有飞一般的感觉。前段时间做了调研,踩了几个坑,这里总结下,希望大家少走弯路。


    本文分别在windows环境和linux环境下介绍如何实现该技术


    使用工具:
    1.JAVA使用的IDE为eclipse
    2.windows环境下C++使用的IDE为visual studio 2010
    3.linux环境下C++使用的编译器为gcc/g++


    二.windows环境下java调用C++代码

    2.1新建java工程,生成相应头文件

    eclipse新建工程名为"jniDemo"的java工程,在包名为com.woniu.Native下新建"NativeCpp.java"类,如下:

    package com.woniu.Native;
    
    public class NativeCpp {
    	public native void fun1();
    	public native int  fun2(int a, int b);
    	public native void fun3(String url1, String url2);
    }

    0?wx_fmt=jpeg


    编译生成.class文件

    进入工程下的targetclasses目录下,执行"javah -jni com.woniu.Native.NativeCpp",运行结果如下:

    0?wx_fmt=jpeg

    此时,会在classes目录下生成"com_woniu_Native_NativeCpp.h"头文件,头文件内容如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_woniu_Native_NativeCpp */
    
    #ifndef _Included_com_woniu_Native_NativeCpp
    #define _Included_com_woniu_Native_NativeCpp
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_woniu_Native_NativeCpp
     * Method:    fun1
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_woniu_Native_NativeCpp_fun1
      (JNIEnv *, jobject);
    
    /*
     * Class:     com_woniu_Native_NativeCpp
     * Method:    fun2
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_woniu_Native_NativeCpp_fun2
      (JNIEnv *, jobject, jint, jint);
    
    /*
     * Class:     com_woniu_Native_NativeCpp
     * Method:    fun3
     * Signature: (Ljava/lang/String;Ljava/lang/String;)V
     */
    JNIEXPORT void JNICALL Java_com_woniu_Native_NativeCpp_fun3
      (JNIEnv *, jobject, jstring, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif


    2.2 c++生成动态库

    vs2010新建工程名为"JniDll"的win32控制台应用程序,win32应用程序向导界面选择 "DLL"

    0?wx_fmt=jpeg

    创建完成后,把2.1中生成的"com_woniu_Native_NativeCpp.h"头文件放入该工程,并把头文件中的#include <jni.h>改为 "jni.h",
    把JDK下include文件夹下的"jni.h"和include下win32文件夹下的"jni_md.h"头文件也一同放入创建的工程中。

    工程目录如下:

    0?wx_fmt=jpeg

    编辑JniDll.cpp源码文件,实现头文件中的函数,如下:

    /********************************************************
    Copyright (C), 2016-2017,
    FileName: 	jni
    Author: 	woniu201
    Email: 		wangpengfei.201@163.com
    Created: 	2017/09/20
    Description:Jni function
    ********************************************************/
    #include "stdafx.h"
    #include "com_woniu_Native_NativeCpp.h"
    #include "stdio.h"
    #include "stdlib.h"
    
    JNIEXPORT void JNICALL Java_com_woniu_Native_NativeCpp_fun1
    	(JNIEnv *, jobject)
    {
    	printf("hello world
    ");
    }
    
    JNIEXPORT jint JNICALL Java_com_woniu_Native_NativeCpp_fun2
    	(JNIEnv *, jobject, jint a, jint b)
    {
    	return a + b;
    }
    
    char* jstringToChar(JNIEnv* env, jstring jstr) {
    	char* rtn = NULL;
    	jclass clsstring = env->FindClass("java/lang/String");
    	jstring strencode = env->NewStringUTF("GB2312");
    	jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    	jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    	jsize alen = env->GetArrayLength(barr);
    	jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    	if (alen > 0) {
    		rtn = (char*) malloc(alen + 1);
    		memcpy(rtn, ba, alen);
    		rtn[alen] = 0;
    	}
    	env->ReleaseByteArrayElements(barr, ba, 0);
    	return rtn;
    }
    
    JNIEXPORT void JNICALL Java_com_woniu_Native_NativeCpp_fun3
    	(JNIEnv *env, jobject, jstring url1, jstring url2)
    {
    	//jstringתchar*
    	char* pUrl1 = jstringToChar(env, url1);
    	char* pUrl2 = jstringToChar(env, url2);
    	printf("url1 = %s
    ", pUrl1);
    	printf("url2 = %s
    ", pUrl2);
    }


    我本机是64位系统,使用的是64位JDK,这里生成的动态库也要生成64位的库,否则调用的时候报如下错误:

    0?wx_fmt=jpeg

    更改vs编译生成64位dll,步骤如下:


    0?wx_fmt=jpeg

    0?wx_fmt=jpeg

    0?wx_fmt=jpeg

    编译生成解决方案,这时候会在工程根目录下,生成"x64文件夹",Debug文件夹下会有动态库"JniDll.dll"

    0?wx_fmt=jpeg

    2.3 java调用dll

    package com.woniu.jniDemo;
    
    import com.woniu.Native.NativeCpp;
    
    public class App 
    {
        public static void main( String[] args )
        {
        	System.load("D:\VS2010\VC\JniDll\x64\Debug\JniDll.dll");
        	NativeCpp nativeCpp = new NativeCpp();
            nativeCpp.fun1();
            System.out.println(nativeCpp.fun2(3, 3));
            nativeCpp.fun3("www.baidu.com", "www.haoservice.cn");
        }
    }


    运行结果如下:

    0?wx_fmt=jpeg


    三.linux(CentOS)环境下java调用C++代码

    3.1 编译环境

    a.安装gcc和g++

      yum install gcc-c++


    b.安装jdk
      去官网上下载jdk安装包,建议使用rpm安装包,会自动配置环境变量。安装完后如下:

    0?wx_fmt=jpeg

      本机的安装目录为:/usr/java/jdk1.8.0_144/,不同版本可能不同。


    这里一定要注意不能安装openjdk,因为openjdk没有include目录,编译时需要用到include目录的头文件。

    3.2 制作动态库(so库)

    a.创建文件夹"jniso",mkdir jniso。


    b.把2.1中生成的头文件"com_woniu_Native_NativeCpp.h"拷贝过来,#include "jni.h"改为#include <jni.h>


    c.新建jni.cpp源文件,添加如下代码:

    #include <jni.h>
    #include "com_woniu_Native_NativeCpp.h"
    #include "stdio.h"
    #include "stdlib.h"
    #include "string.h"
    
    JNIEXPORT void JNICALL Java_com_woniu_Native_NativeCpp_fun1 (JNIEnv *, jobject)
    {
            printf("hello world
    ");
    }
    
    JNIEXPORT jint JNICALL Java_com_woniu_Native_NativeCpp_fun2
            (JNIEnv *, jobject, jint a, jint b)
    {
            return a + b;
    }
    
    char* jstringToChar(JNIEnv* env, jstring jstr) {
            char* rtn = NULL;
            jclass clsstring = env->FindClass("java/lang/String");
            jstring strencode = env->NewStringUTF("GB2312");
            jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
            jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
            jsize alen = env->GetArrayLength(barr);
            jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
            if (alen > 0) {
                    rtn = (char*) malloc(alen + 1);
                    memcpy(rtn, ba, alen);
                    rtn[alen] = 0;
            }
            env->ReleaseByteArrayElements(barr, ba, 0);
            return rtn;
    }
    
    JNIEXPORT void JNICALL Java_com_woniu_Native_NativeCpp_fun3
            (JNIEnv *env, jobject, jstring url1, jstring url2)
    {
            char* pUrl1 = jstringToChar(env, url1);
            char* pUrl2 = jstringToChar(env, url2);
            printf("url1 = %s
    ", pUrl1);
            printf("url2 = %s
    ", pUrl2);
    }


    d.编译,生成动态库
    g++ -fPIC -c jni.cpp -I /usr/java/jdk1.8.0_144/include/ -I /usr/java/jdk1.8.0_144/include/linux/
    g++ -shared jni.o -o jni.so

    3.3 java调用jni.so

    import com.woniu.Native.NativeCpp;
    
    public class App 
    {
        public static void main( String[] args )
        {
        	//windows环境下加载库
        	//System.load("D:\VS2010\VC\JniDll\x64\Debug\JniDll.dll");
        	
        	//linux下加载库
        	System.load("/mnt/hgfs/svn/svn/Demo/jniso/jni.so");
        	
        	NativeCpp nativeCpp = new NativeCpp();
            nativeCpp.fun1();
            System.out.println(nativeCpp.fun2(3, 3));
            nativeCpp.fun3("www.baidu.com", "www.haoservice.cn");
        }
    }


    运行结果如下:

    0?wx_fmt=jpeg

    往期推荐

     


    【技术篇】 


    【技术篇】 


    【生活篇】

    0?wx_fmt=jpeg

  • 相关阅读:
    http协议
    web及时通信传输的几种方法
    头像上传可预览实现代码
    JavaScript对象基础知识总结
    js异步流程的promise及generator
    数据整理
    前端技术分类整理
    HTML随笔
    前端内网穿透,localtunnel
    CSS记录
  • 原文地址:https://www.cnblogs.com/Java-Road/p/11824700.html
Copyright © 2020-2023  润新知