JNA的简单使用

以前从效率上考虑,做native调用都是通过jni实现,其中的一些障碍我想开发过jni的人都知道,比如你要复用老的代码库,为了适应Jni的机制需要再做一次封装,这其中要涉及到jni的内存管理规则,和Java代码的交互,数据类型的转换,在c/c++复杂数据类型上是很棘手的。当然这些都是可以解决的,就看个人的开发思路。

近日研究了JNA(java native access),这种机制调用dll,so相对于Jni是非常方便的,你不用再做wrapper,做到只要有头文件你想调用那个接口就声明那个接口(做过.net的人是很熟悉的),当然这种好用的模式需要你下载jna.jar这个包加入你的project,说穿了别人为你做了很多转换的工作,效率肯定没有直接调用jni高。

下面就怎么使用JNA做说明,涉及到java代码调用native、native回调java代码(回调是很有用的,存在于很多库中!)、结构体参数的使用(感谢jna.jar的开发者,如果dll,so接口是自己设计的请避免结构体、复杂数据结构!)。


我的测试环境是win7 32bit旗舰版 + ecplise + jdk1.7, jna.jar请自行下载,网上很多。

1.需要引用的类,可以根据自己的情况而定。

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Callback; // 如果是cdecl模式回调要用
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;// stdcall回调模式要用


2.在Interface中声明要调用的接口(截取的主体代码)。


// 登录接口使用的结构体java声明

public static class LoginInfo extends Structure {
public static class ByValue extends LoginInfo implements Structure.ByValue {}  // 这行很重要,否则你的数据到了native中完全是乱码
public byte user[] = new byte[64];
public byte pwd[] = new byte[64];
public byte host[] = new byte[64];
public int port;
}


// 声明natvie中的接口

public interface CDeviceAPI extends Library // 如果接口是stdcall约定请使用 StdCallLibrary
{
// event call interface
interface JavaCallbackEvent extends StdCallCallback// 如果回调函数是stdcall约定使用这种模式,否则使用与之对应的
{
void CallbackEvent(int type, Pointer pUser);// Pointer转c的void*
}



int init();
int cleanup();
int GetAPIVer();
int Login(LoginInfo.ByValue info);
int Logout(int userid);
int set_event_callback(int userid, JavaCallbackEvent cbfun, Pointer pUser);
}


3.上面已经完成了部分接口的声明,下面实际的调用。

// 加载dll、so,Windows环境下直接放入工程目录下即可,like unix环境放入/lib或者usr/lib即可。

CDeviceAPI lib = (CIPCAPI)Native.loadLibrary("testlib", CDeviceAPI .class);
 
CDeviceAPI .JavaCallbackEvent cbEvent = new CDeviceAPI .JavaCallbackEvent() {

@Override
public void CallbackEvent(int type, Pointer pUser) {
// TODO Auto-generated method stub
System.out.println("Java layer Event=" + type);
}
};

long ver = lib.GetAPIVer();
System.out.println("ver=" + ver);

// init
lib.init();
// login data
LoginInfo.ByValue info = new LoginInfo.ByValue();
info.user = new byte[64];
for(int i=0; i < "admin".length(); i++) {
info.user[i] = (byte) "admin".charAt(i);
}

userid = lib.Login(info, 1);
System.out.println("userid=" + userid);

lib.set_event_callback(userid, cbEvent, null);// 设置回调
System.out.println("wait to connect device.");



Thread.sleep(20000);

lib.cleanup(userid);


4。以上1,2,3点就是核心代码,其实只要关注c,java/jna数据类型的转换,结构体的转换,回调,接口参数入栈的约定,开发就基本没有什么问题。

jna开发确实比jni方便,如果适合自己的项目建议使用,毕竟jni加个native api Java层就要改的麻烦没有了。

如果你的接口有很多结构体参数,那不管是jni,jna在java层表达都比较麻烦,建议使用json好些。


Java—C和操作系统数据类型的对应表

Java Type

C Type

Native Representation

boolean

int

32-bit integer (customizable)

byte

char

8-bit integer

char

wchar_t

platform-dependent

short

short

16-bit integer

int

int

32-bit integer

long

long long, __int64

64-bit integer

float

float

32-bit floating point

double

double

64-bit floating point

Buffer
Pointer

pointer

platform-dependent (32- or 64-bit pointer to memory)

[] (array of primitive type)

pointer
array

32- or 64-bit pointer to memory (argument/return)
contiguous memory (struct member)

除了上面的类型,JNA还支持常见的数据类型的映射。

String

char*

NUL-terminated array (native encoding or jna.encoding)

WString

wchar_t*

NUL-terminated array (unicode)

String[]

char**

NULL-terminated array of C strings

WString[]

wchar_t**

NULL-terminated array of wide C strings

Structure

struct*
struct

pointer to struct (argument or return) (or explicitly)
struct by value (member of struct) (or explicitly)

Union

union

same as Structure

Structure[]

struct[]

array of structs, contiguous in memory

Callback

(*fp)()

function pointer (Java or native)

NativeMapped

varies

depends on definition

NativeLong

long

platform-dependent (32- or 64-bit integer)

PointerType

pointer

same as Pointer


你可能感兴趣的:(Android)