Linux环境下JNI say hello的例子

Linux环境下JNI say hello的例子

  参考文章:https://www.ibm.com/developerworks/cn/java/l-linux-jni/

  JNI是Java Native Interface的缩写,JVM可以通过JNI去调用本地(Native)方法,目前做后端开发的还是挺少会去写JNI的,因为本身JNI就有一定的性能开销。目前来说可能客户端用的比较多,下面说下碰到的使用场景吧:

  • android一些计算密集型的代码限于机能可能会通过JNI来去实现
  • 还有一些对安全要求较高的加密解密的代码会放到so中去,毕竟java代码反编译比较容易,本身so文件反编译就比较麻烦,又可以继续通过加固来增加反编译的难度
  • 一些跨平台的游戏引擎比如cocos是用c++开发的,编译到安卓平台需要通过JNI来实现

  下面通过一个简单的say hello的例子来简单了解一下jni

第一步实现一个Java类,Hello 它提供SayHello方法:
public class Hello {
    static {
        try {
            // 需要加载的动态链接库
            System.loadLibrary("hello");
        } catch (UnsatisfiedLinkError e) {
            System.err.println("Cannot load hello library:\n" + e.toString());
        }   
    }   

    // 要使用的本地方法的声明
    public native void SayHello(String str);
}

然后通过javac编译生成Hello.class文件

第二步,生成本地动态链接库

1、要为Hello这个类生成Java本地接口的头文件,JDK中为我们提供了javah来去生产JNI的头文件,不再需要我们再去写头文件通过如下命令:

%JAVA_HOME%/bin/javah Hello

之后可以看到当前目录多了一个Hello.h文件,内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */

#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Hello
 * Method:    SayHello
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_Hello_SayHello
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

2、创建Hello.cpp文件实现Hello.h中声明的函数,内容如下:

#include "Hello.h"
#include <stdio.h>

// Hello.h中函数声明的实现
JNIEXPORT void JNICALL Java_Hello_SayHello
(JNIEnv* env, jobject arg, jstring instring) {
    // 从 instring字符串取得指向字符串 UTF 编码指针
    const jbyte* str = (const jbyte*) env->GetStringUTFChars(instring, JNI_FALSE);
    printf("Hello, %s\n", str);
    // 通知虚拟机本地代码不再需要通过 str 访问 Java字符串。
    env->ReleaseStringUTFChars(instring, (const char*)str);
    return;
}

所有的 JNI 调用都使用了 JNIEnv * 类型的指针,习惯上在 CPP 文件中将这个变量定义为 env,它是任意一个本地方法的第一个参数。env 指针指向一个函数指针表,在c++中可以直接用"->"操作符访问其中的函数。

jobject 指向在此 Java 代码中实例化的 Java 对象 LocalFunction 的一个句柄,相当于 this 指针。

后续的参数就是本地调用中有 Java 程序传进的参数,本例中只有一个 String 型参数。 对于字符串型参数,因为在本地代码中不能直接读取 Java 字符串,而必须将其转换为 C /C++ 字符串或 Unicode。
3、编译生成动态链接库
使用gcc编译时,需要声明jdk include的头文件地址, 通过 -fPIC -c 来生成.o的目标文件

gcc -I/usr/lib/jvm/java/include -I/usr/lib/jvm/java/include/linux -fPIC -c Hello.cpp

生成 libhello.so文件(动态链接库需要lib开头),命令如下:

gcc -shared -o libhello.so Hello.o

设置环境变量,让程序能找到so文件.

export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH

4、编译一个简单的java代码来测试我们实现的本地方法。

public class ToSay {

    public static void main(String[] args) {
        Hello hello = new Hello();
        hello.SayHello("张三");
    }   

}

使用javac编译ToSay.java,生成ToSay.class
执行 java ToSay后我们会看到屏幕上出现 Hello, 张三

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值