JNA初体验

  |   0 评论   |   134 浏览

背景

JNI(Java Native Interface)是让Java来调用C的方法。JNA是一个常用的实现JNI功能的库。

JNA可以很好的支持C,但是目前作者还没有找到让JNA支持C++的方法。

HelloWorld初体验

第一个例子,当然是HelloWorld。下面调用C标准库中的printf方法,来输出HelloWorld

POM依赖

		<dependency>
			<groupId>net.java.dev.jna</groupId>
			<artifactId>jna</artifactId>
			<version>5.2.0</version>
		</dependency>

编写 Java 代码

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public class HelloWorld {

	// This is the standard, stable way of mapping, which supports extensive
	// customization and mapping of Java to native types.

	public interface CLibrary extends Library {
		CLibrary INSTANCE = (CLibrary) Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);

		void printf(String format, Object... args);
	}

	public static void main(String[] args) {
		CLibrary.INSTANCE.printf("Hello, World\n");
		for (int i = 0; i < args.length; i++) {
			CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
		}
	}
}

运行结果

Hello, World

Direct Mapping体验

使用Direct Mapping方法,可以通过定制JNI,来显著提高性能。

优点是不再需要接口定义,只需要native来声明动态库的函数。

同时在调用函数之前,需要调Native.register方法来注册(加载)动态库。

Java 代码

import com.sun.jna.Native;
import com.sun.jna.Platform;

public class DirectMapping {
	public static native double cos(double x);

	public static native double sin(double x);

	static {
		Native.register(Platform.C_LIBRARY_NAME);
	}

	public static void main(String[] args) {
		System.out.println("cos(0)=" + cos(0));
		System.out.println("sin(0)=" + sin(0));
	}
}

结果

cos(0)=1.0
sin(0)=0.0

自动接口生成

jnaerator可以将c的头文件自动转换为java接口。

jnaerator可以以命令行形式运行,也可以通过java代码执行。

本文以命令行形式运行为例来说明。

准备工作

先编写一个最简单的so包。

.
├── include
│   └── hello.h
├── lib
├── source
│   └── hello.cpp
└── src
    └── main.cpp

准备 hello.h 文件

$cat include/hello.h
void show();

准备 hello.cpp 文件

$cat source/hello.cpp
#include "../include/hello.h"
#include <stdio.h>

void show() {
    printf("hello world\n");
}

编译 libhello.so

$g++ source/hello.cpp -fPIC -shared -o lib/libhello.so

编写 main.cpp 文件

$cat src/main.cpp
#include "../include/hello.h"

int main(int argc, char** argv){
    show();
}

编译 main.cpp 文件

$g++ src/main.cpp -L lib/ -l hello -o main

运行 main 文件

直接运行,会提示找不到 hello

$./main
./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

所以通过LD_LIBRARY_PATH来指定hello库路径,来运行

$LD_LIBRARY_PATH=lib/ ./main
hello world

命令行形式运行

下载

wget http://central.maven.org/maven2/com/nativelibs4java/jnaerator/0.12/jnaerator-0.12-shaded.jar

运行

$java -jar jnaerator-0.12-shaded.jar -v -runtime JNA -mode Maven -mavenGroupId com.abeffect.jna -mavenArtifactId testJNA -o jna_code -package com.abeffect.jna.core -f -library Hello lib/libhello.so include/hello.h

运行结果

jna_code/
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── abeffect
        │           └── jna
        │               └── core
        │                   └── HelloLibrary.java
        └── resources
            └── lib
                └── linux_x64
                    └── libhello.so

生成的接口代码

package com.abeffect.jna.core;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
/**
 * JNA Wrapper for library <b>Hello</b><br>
 * This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
 * a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
 * For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
 */
public interface HelloLibrary extends Library {
        public static final String JNA_LIBRARY_NAME = "Hello";
        public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(HelloLibrary.JNA_LIBRARY_NAME);
        public static final HelloLibrary INSTANCE = (HelloLibrary)Native.loadLibrary(HelloLibrary.JNA_LIBRARY_NAME, HelloLibrary.class);
        /**
         * Original signature : <code>void show()</code><br>
         * <i>native declaration : include/hello.h:1</i>
         */
        void show();
}

常见问题

Java发现library库

怎么样让Java代码发现使用的library库呢?

  1. 使用环境变量jna.library.path,这个为jna专用的,功能类似于java.library.path
  2. 在启动前设置环境变量,为Windows下的PATH,Linux下的LD_LIBRARY_PATH,或者OSX下的DYLD_LIBRARY_PATH
  3. 在java的classpath中,在{OS}-{ARCH}/{LIBRARY}下放入对应的库,如win32-x64, linux-amd64或者darwin。这样在load的时候,会自动从jar包在加载出来。

同步访问

可以使用 Native.synchronizedLibrary 来达到同一时刻只有一个线程来访问这个库。

Kernel32 INSTANCE = (Kernel32)
    Native.load("kernel32", Kernel32.class);
// Optional: wraps every call to the native library in a
// synchronized block, limiting native calls to one at a time
Kernel32 SYNC_INSTANCE = (Kernel32)
    Native.synchronizedLibrary(INSTANCE);

参考

评论

发表评论

validate