JNA相关知识

使用JNA简单调用DLL里的函数
1、在VC下创建一个动态链接库项目testJNA

2、在头文件里声明函数

extern "C" _declspec(dllexport) int add(int first, int second);


红色字体部分是必须的,包括定义结构体时也需要。应该是说此函数是发布的。


3、在源码里实现函数

int add(int first, int second) {
	printf("(c) test jna : %d + %d = %d", first, second, first + second);
	return first + second;
}



4、生成dll文件


5、定义一个表示链接库的接口
接口TestJnaLib继承自com.sun.jna.Library,此接口有一个实例 

TestJnaLib INSTANCE = (TestJnaLib)Native.loadLibrary("testJNA.dll", TestJnaLib.class);

此实例由jna通过反射自动生成。

6、定义对应dll里的方法

int add(int first, int second);




7、调用本地方法

TestJnaLib.INSTANCE.add(3, 5);









Jna回调Java方法:

1、在C语言部分定义带回调函数的函数

extern "C" _declspec(dllexport) void methodWithCallback(int (*fp)(int left, int right), int left, int right);


红色加粗部分是函数指针。

2、Java部分定义一个回调接口
必须继承自com.sun.jna.Callback接口

public interface FunCallBack extends Callback {
	int invoke(int left, int right);
}


Invoke方法里的参数顺序与C函数的对应

3、定义回调接口的实现

public class CallbackFunImpl implements FunCallBack {
	@Override
	public int invoke(int left, int right) {
		System.out.printf("in java :%d + %d = %d\n", left, right, left + right);
		return left + right;
	}
}



4、在表示链接库实现的接口里定义要回调的本地函数

void methodWithCallback(Callback callback, int left, int right);

本地函数的函数指针用Callback 接口替代。

5、调用带函数指针的本地函数

TestJnaLib.INSTANCE.methodWithCallback(new CallbackFunImpl(), 4, 6)

 

1、编写需要使用Jni的Java类文件

Java代码 复制代码  收藏代码
public class JniCall {
	static {
		System.loadLibrary("testJNA");
	}
	
	public native static int add(int first, int second);
	
	public static void main(String[] args) {
		int first = 3;
		int second = 4;
		
		System.out.printf("print in java : %d + %d = %d", first, second, add(first, second));
	}
}


本地方法必须声明为native。

2、编译出class文件,用javah从class文件生成C的头文件JniCall.h

C代码 复制代码  收藏代码
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JniCall */

#ifndef _Included_JniCall
#define _Included_JniCall
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniCall
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_JniCall_add
  (JNIEnv *, jclass, jint, jint);


typedef struct Student {
	char * name;
	int age;
	int height;

}StudentObj;

#ifdef __cplusplus
}
#endif
#endif




3、在VC下建立一个动态链接库项目testJNA

倒数第二个。

4、把生成的JniCall.h和$JAVA_HOME/include/jni.h、$JAVA_HOME/include/win32jni_md.h拷贝到vc项目testJNA的目录下


5、编写C的本地实现

C代码 复制代码  收藏代码
#include "stdafx.h"


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    return TRUE;
}

JNIEXPORT jint JNICALL Java_JniCall_add(JNIEnv *, jclass, jint first, jint second) {
	printf("print in c    : %d + %d = %d \n", first, second, first + second);
	return first + second;
}



6、构建testJNA项目,生成testJNA.dll文件


7、把testJNA.dll拷贝到$JAVA_HOME/jre/bin目录下

8、运行Java类,调用本地方法
D:\Java\jdk1.6.0_02\bin>java JniCall
print in c    : 3 + 4 = 7
print in java : 3 + 4 = 7
D:\Java\jdk1.6.0_02\bin>



JNA相关知识:
使用JNA调用原生函数的模式:
JNA不实用native关键字。
JNI使用native关键字,使用一个个java方法来代表外部的原生函数。
JNA使用一个java接口来代表一个动态链接库发布的所有函数。
对于不需要的原生函数,可以不在java接口中声明java方法原型。

使用JNI,需要使用System.loadLibrary方法,把专门为JNI编写的动态链接库载入进来,这个动态链接库实际上是真正需要的动态链接库的代理。
使用JNA类库的Native类的loadLibrary方法,是直接把需要的动态链接库载入进来。使用JNA不需要编写作为代理的动态链接库,不需要编写一行原生代码。


跨平台、跨语言调用原则:
尽量使用基本、简单的数据类型;
尽量少跨平台、跨语言传递数据!

Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,JVM的GC不能管理,会造成内存碎片。




C语言的结构体是一个严格的规范,定义了内存的次序。因此,JNA中模拟的结构体的变量顺序绝对不能错。

Java调用动态链接库中的C函数,实际上就是把一段内存作为函数的参数传递给C函数。动态链接库以为这个参数就是C语言传过来的参数。

Structure类的write()方法会把结构体的所有字段固定住,是原生函数可以访问。


JNI技术是双向的,既可以从Java代码中调用原生函数,也可以从原生函数中直接创建Java虚拟机,并调用Java代码。

原生函数可以通过函数指针实现函数回调,调用外部函数来执行任务。这就是策略模式。



Callback:
任务回调定义必须继承自com.sun.jna.Callback接口,子类必须定义单个公有方法或一个名为callback的公有方法。必须持有到回调对象的一个存活引用。一个回调应该不抛出异常。

com.sun.jna.Library:
代表本地链接库的定义。可用如下形式定义:

MyNativeLibrary INSTANCE = (MyNativeLibrary)Native.loadLibrary("mylib", MyNativeLibrary.class);


虽然结构体和结构体字段的名字可以是任意的,但他们应该尽可能接近本地定义。参数名也一样。
此接口支持在Java端多线程、并发调用本地方法。如果本地类库不是线程安全的,可用

Native.synchronizedLibrary(com.sun.jna.Library)

来阻止多线程同时访问本地代码。


链接库的搜索路径:
jna.library.path 用户定义的路径;
Jna.platform.library.path 平台定义的路径,


com.sun.jna.Structure:
代表本地结构体的对等Java类。
当作为参数或返回值使用时,这个类被传递给struct * (指针);当作为另一个结构体的字段时,它被传递给struct (值传递)。
标记接口Structure.ByReference和Structure.ByValue被用来提醒默认的行为。

Structure属性传递给本地字段必须是public的。可以由下面可选的修饰符:
volatile JNA不会修改字段,除非被要求,通过writeField(String);
final   JNA会通过read()覆写字段,字段在java这边是不可变的。

Structure属性的顺序与类型必须与本地结构体的字段一一对应。

 

 

 

你可能感兴趣的:(jni,dll,JNA,so)