Java调用本地方法(JNI浅谈)
(2006-11-27 14:55:36)
转载▼
分类: Java类文章 |
本人在项目开发实践中的总结和体会
前段时间公司要求将指纹应用到web上,之前都是用delphi开发并实施,采用的是C/S模式,但是现在已经无法满足需求,只能应用B/S模式.但是使用B/S模式的局限性体现在三方面,
其一:采集指纹数据和指纹比对身份认证还是无法直接利用web实现,毕竟和底层设备打交道是很难在web上实现的,考虑还是使用delphi组件采集和比对指纹,人员信息和其他业务利用web传输显示.
其二:数据库中的指纹数据都是压缩后的bmp文件,而数据解压缩是调用指纹算法研究部门用VC写的一个DLL库,在delphi中调用DLL是比较简单 的,但是如果将指纹数据显示到web上的话就必须利用java调用解压缩DLL文件,而java调用本地方法最简单的方法只能是JNI(Java Native Interface ).
下面主要介绍一下我是如何利用JNI调用已经写好的DLL文件.
思路:JNI调用本地方法的话,必须先在JAVA中声明本地方法,然后用VC实现本地方法,而在VC中的本地方法必须符合JNI的命名规则才会被JNI识 别并调用,但是现在是已经有一个用VC写好的DLL,当然我不可能按照JNI的规则重写该DLL,不现实!我想应该可以用VC将已经写好的DLL封装一 下,即我写一个中间DLL符合JNI规则的DLL应该可以的.
第一步: JAVA中声明本地方法
package com.cchongda.decompress;
public class TestDecompress {
static{
System.loadLibrary("testdecompress");//加载动态库testdecompress.dll
}
public native byte[] decompress(byte[] inbs,int hh,int ww);
static{
System.loadLibrary("testdecompress");//加载动态库testdecompress.dll
}
public native byte[] decompress(byte[] inbs,int hh,int ww);
}
第二步: 在定义一个类,该类中只声明两个byte[] 一个用来存放压缩的指纹数据,一个用来接受调用解压缩方法解压后的指纹数据.
package com.cchongda.decompress;
public class InputByte {
public byte[] inbs;
public byte[] outbs;
public InputByte() {
}
}
public byte[] inbs;
public byte[] outbs;
public InputByte() {
}
}
第三步:编译TestDecompress类,生成TestDecompress.class文件,然后利用javah -classpath 类路径 包名.类名编译TestDecompress.class生成包名_类名.h格式的C头文件,我的头文件是 com_cchongda_decompress_TestDecompress.h
该将该头文件copy到VC++6.0下的VC98目录下的include文件夹下或者放到你的DLL工程目录下,同时将 D:\j2sdk1.4.2_13\include\下的jni.h和win32目录下的jni_md.h两个头文件都copy到VC++6.0下的 VC98目录下的include文件夹下或者放到你的DLL工程目录下.
类生成的TestDecompress.h头文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_cchongda_decompress_TestDecompress */
#include
/* Header for class com_cchongda_decompress_TestDecompress */
#ifndef _Included_com_cchongda_decompress_TestDecompress
#define _Included_com_cchongda_decompress_TestDecompress
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_cchongda_decompress_TestDecompress
* Method: decompress
* Signature: ([BII)[B
*/
#define _Included_com_cchongda_decompress_TestDecompress
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_cchongda_decompress_TestDecompress
* Method: decompress
* Signature: ([BII)[B
*/
//这个地方就是java中声明的本地方法原型
JNIEXPORT jbyteArray JNICALL Java_com_cchongda_decompress_TestDecompress_decompress
(JNIEnv *, jobject, jbyteArray, jint, jint);
JNIEXPORT jbyteArray JNICALL Java_com_cchongda_decompress_TestDecompress_decompress
(JNIEnv *, jobject, jbyteArray, jint, jint);
#ifdef __cplusplus
}
}
//
#endif
#endif
#endif
#endif
第四步:封装已经写好的dll,在VC中新建一个win32 Dynamic-LinkLibrary工程,工程名与java中将要加载的dll名称一样为"testdecompress",在生成的testdecompress.cpp文件中敲入如下代码:(红色为我敲入的代码)
#include "stdafx.h"
(1) #include "com_cchongda_decompress_TestDecompress.h"
(2)extern "C" int _stdcall Decompress(unsigned char *outimg,char* ezwimg,int hh,int ww);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
(1) #include "com_cchongda_decompress_TestDecompress.h"
(2)extern "C" int _stdcall Decompress(unsigned char *outimg,char* ezwimg,int hh,int ww);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
(3)JNIEXPORT jbyteArray JNICALL Java_com_cchongda_decompress_TestDecompress_decompress(JNIEnv * env,jobject obj,jbyteArray barr,jint hh,jint ww)
{
int i =1;
unsigned char * buffer = new BYTE[92160];
jbyteArray temparr;
temparr = env->NewByteArray(92160);
char * parr;
jbyte *arr = env->GetByteArrayElements(barr,0);
parr = (char *)arr;
/*FILE *fp;
fp = fopen("C:\\11.dat","w");
fwrite(parr,1,4610,fp);
fclose(fp);*/
i=Decompress(buffer,parr,hh,ww);
//printf("%d",i);
env->SetByteArrayRegion(temparr, 0, 92160, (const signed char *)buffer);
env->ReleaseByteArrayElements(barr, (signed char *)parr, 0);
return temparr;
}
{
int i =1;
unsigned char * buffer = new BYTE[92160];
jbyteArray temparr;
temparr = env->NewByteArray(92160);
char * parr;
jbyte *arr = env->GetByteArrayElements(barr,0);
parr = (char *)arr;
/*FILE *fp;
fp = fopen("C:\\11.dat","w");
fwrite(parr,1,4610,fp);
fclose(fp);*/
i=Decompress(buffer,parr,hh,ww);
//printf("%d",i);
env->SetByteArrayRegion(temparr, 0, 92160, (const signed char *)buffer);
env->ReleaseByteArrayElements(barr, (signed char *)parr, 0);
return temparr;
}
(1)该语句是将class文件生成的.h头文件引进来,因为在那个头文件有对在java中声明的本地方法的原型.
(2)该语句是将已经写好的数据解压缩dll中的解压缩方法原型的声明,在项目中要将已写好的dll导入项目中,我采用的是隐式加载即引入头文件(在.cpp中声明函数原型也可)和.lib文件,图中红色的为引进来的.lib文件
由此即实现了封装已写好的Exwdll.dll,编译该testdecompress.cpp文件后,在Debug文件下生成一个tesedecompress.dll文件,将该文件copy到java项目下开始测试.
第五步:写java测试代码
(1)数据库连接类DbConnect
package com.cchongda.decompress;
import java.sql.*;
public class DbConnect {
public DbConnect() {
}
public Connection conn=null;
public Statement stat=null;
public PreparedStatement pstat=null;
public ResultSet rs=null;
private String Driver="com.mysql.jdbc.Driver";
private String url="jdbc:mysql://192.101.1.138:3306/fingerdb";
{
try{
Class.forName(Driver).newInstance();
}
catch(java.lang.IllegalAccessException iae){
iae.printStackTrace();
}
catch(java.lang.ClassNotFoundException cnf){
cnf.printStackTrace();
}
catch(java.lang.InstantiationException ie){
ie.printStackTrace();
}
}
public class DbConnect {
public DbConnect() {
}
public Connection conn=null;
public Statement stat=null;
public PreparedStatement pstat=null;
public ResultSet rs=null;
private String Driver="com.mysql.jdbc.Driver";
private String url="jdbc:mysql://192.101.1.138:3306/fingerdb";
{
try{
Class.forName(Driver).newInstance();
}
catch(java.lang.IllegalAccessException iae){
iae.printStackTrace();
}
catch(java.lang.ClassNotFoundException cnf){
cnf.printStackTrace();
}
catch(java.lang.InstantiationException ie){
ie.printStackTrace();
}
}
public Connection getConn() {
try{
this.conn = java.sql.DriverManager.getConnection(url, "root", "");
}
catch(java.sql.SQLException e){
e.printStackTrace();
}
return conn;
}
public Statement getStat(){
Statement stat = null;
try{
stat = this.getConn().createStatement();
}
catch(SQLException sqle){
sqle.printStackTrace();
}
return stat;
}
public ResultSet getRs(String sql){
// String sql="select htzw1 from zwzpxxb where sbh=''";
ResultSet rs=null;
try{
rs = stat.executeQuery(sql);
}
catch(java.sql.SQLException sqle){
sqle.printStackTrace();
}
return rs;
}
}
(2)主类提供入口函数
return conn;
}
public Statement getStat(){
Statement stat = null;
try{
stat = this.getConn().createStatement();
}
catch(SQLException sqle){
sqle.printStackTrace();
}
return stat;
}
public ResultSet getRs(String sql){
// String sql="select htzw1 from zwzpxxb where sbh=''";
ResultSet rs=null;
try{
rs = stat.executeQuery(sql);
}
catch(java.sql.SQLException sqle){
sqle.printStackTrace();
}
return rs;
}
}
(2)主类提供入口函数
package com.cchongda.decompress;
import java.sql.*;
import java.io.*;
public class Test {
public Test() {
}
public static void main(String[] args){
FileOutputStream fos = null;
TestDecompress td = new TestDecompress();
InputByte ib = new InputByte();
int hh = 360;
int ww = 256;
File file = new File("C:/uuu.bmp");
try{
fos = new FileOutputStream(file);
}
catch(Exception e){
e.printStackTrace();
}
DbConnect dbc = new DbConnect();
try{
dbc.stat = dbc.getConn().createStatement();
}catch(java.sql.SQLException sqle){
sqle.printStackTrace();
}
String sql1= "select wjt from wjt";
String sql2 = "select htzw1 from zwzpxxb where sbh='A026010239'";
Blob blob = null;
ResultSet rs1 = dbc.getRs(sql1);
try{
if (rs1.next()) {
blob = rs1.getBlob("wjt");
System.out.println("文件头的字节数:"+blob.length());
}
fos.write(blob.getBytes(1,(int)blob.length()),0,(int)blob.length());
}catch(Exception e){
e.printStackTrace();
}
blob =null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ResultSet rs2 = dbc.getRs(sql2);
try{
if (rs2.next()) {
blob = rs2.getBlob("htzw1");
System.out.println("文件体的字节数:"+blob.length());
bos.write(blob.getBytes(1,(int)blob.length()),0,(int)blob.length());
ib.inbs=bos.toByteArray();
System.out.println("进入的字节数"+ib.inbs.length);
// System.out.print(ib.outbs.length);
bos.close();
}
}catch(Exception e){
e.printStackTrace();
}
import java.io.*;
public class Test {
public Test() {
}
public static void main(String[] args){
FileOutputStream fos = null;
TestDecompress td = new TestDecompress();
InputByte ib = new InputByte();
int hh = 360;
int ww = 256;
File file = new File("C:/uuu.bmp");
try{
fos = new FileOutputStream(file);
}
catch(Exception e){
e.printStackTrace();
}
DbConnect dbc = new DbConnect();
try{
dbc.stat = dbc.getConn().createStatement();
}catch(java.sql.SQLException sqle){
sqle.printStackTrace();
}
String sql1= "select wjt from wjt";
String sql2 = "select htzw1 from zwzpxxb where sbh='A026010239'";
Blob blob = null;
ResultSet rs1 = dbc.getRs(sql1);
try{
if (rs1.next()) {
blob = rs1.getBlob("wjt");
System.out.println("文件头的字节数:"+blob.length());
}
fos.write(blob.getBytes(1,(int)blob.length()),0,(int)blob.length());
}catch(Exception e){
e.printStackTrace();
}
blob =null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ResultSet rs2 = dbc.getRs(sql2);
try{
if (rs2.next()) {
blob = rs2.getBlob("htzw1");
System.out.println("文件体的字节数:"+blob.length());
bos.write(blob.getBytes(1,(int)blob.length()),0,(int)blob.length());
ib.inbs=bos.toByteArray();
System.out.println("进入的字节数"+ib.inbs.length);
// System.out.print(ib.outbs.length);
bos.close();
}
}catch(Exception e){
e.printStackTrace();
}
ib.outbs= td.decompress(ib.inbs,hh,ww);
try{
System.out.println("解压缩后的字节数:"+ib.outbs.length);
fos.write(ib.outbs, 0, ib.outbs.length);
fos.flush();
fos.close();
}
catch(Exception e){
e.printStackTrace();
}
try{
System.out.println("解压缩后的字节数:"+ib.outbs.length);
fos.write(ib.outbs, 0, ib.outbs.length);
fos.flush();
fos.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
该过程是从数据库中读取一个未压缩的指纹数据文件头和压缩的指纹数据文件体,调用解压缩方法解压后将完整的指纹数据输出到c:\uuu.bmp,输出结果如图所示:
}
该过程是从数据库中读取一个未压缩的指纹数据文件头和压缩的指纹数据文件体,调用解压缩方法解压后将完整的指纹数据输出到c:\uuu.bmp,输出结果如图所示:
至此本人的JNI调用已经写好的DLL文件测试成功,并与不久将此用于项目开发中.