最近协助同事解决了JNI调用及回调问题,在此做个记录,备忘。
问题是这样子的:同事要开发一个java程序,用来控制摄像头的抓拍,且要通过回调字节流的方式,将抓拍的图片吐给java模块,然后再调人脸识别服务。期间遇到不少问题,最开始用JNA实现了整体功能,但是测试过程发现回调总是不稳定,照片有些时候传不回来。无耐之下,只要改用JNI再试,谁知JNI果然比JNA复杂,又是各种问题,不过还好问题都解决了,在此总结下JNI调用及回调流程。
JNI使用步骤:
1、编写Java代码,其中要注明调用DLL的native方法;
2、通过javah命令,针对java代码生成c头文件;
3、基于c头文件,编写对应的函数实现,一般通过在此实现中调用真正需要交互的dll函数;
完成这3步,基本就可以完成dll调用了。对于回调,需要在c代码中,基于jni api获取要调用的类名、方法名、参数等,特别要注意参数类型的对应关系。
实例分享:
1、编写Java类
import java.io.FileOutputStream;
import java.io.IOException;
//摄像机控制类
public class CameraCtl {
//加载dll库,注意需要将库放入项目根目录,或者设置native library路径到dll目录
static {
System.loadLibrary("HwCameraSdk");
}
//映射到C的本地方法
public native void startHwCamera();
//C的回调方法,通过参数传递字节数组,其实是图片信息
public void hwCameraCallback(byte[] byteAry) {
FileOutputStream out = null;
try {
out = new FileOutputStream("./test3.jpg");
out.write(byteAry);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//用于测试的回调
public void hwCameraCallback(boolean[] booleanAry) {
for (int i = 0; i < booleanAry.length; i++) {
System.out.println(booleanAry[i]);
}
}
//用于测试的无参回调
public void hwCameraCallback() {
System.out.println("触发无参回调");
}
public static void main(String args[]) {
(new CameraCtl()).startHwCamera();
}
}
2、针对第1步的java代码,通过javah工具生成c头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class upup_ajni_CameraCtl */
#ifndef _Included_upup_ajni_CameraCtl
#define _Included_upup_ajni_CameraCtl
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: upup_ajni_CameraCtl
* Method: startHwCamera
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_upup_ajni_CameraCtl_startHwCamera
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
3、编写具体实现,需要引入jni.h,jni_md.h代码结果如下:
具体实现:
#include "jni.h"
#include "CameraCtl.h"
#include
#include
using namespace std;
void writeFile(char* msg){
FILE *fp = NULL;
fp = fopen("./test.txt", "w+");
fprintf(fp, msg);
//fputs("This is testing for fputs...\n", fp);
fclose(fp);
}
void readFile(){
FILE *fp = NULL;
char buff[255];
fp = fopen("./test.txt", "r");
fscanf(fp, "%s", buff);
printf("1: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("2: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("3: %s\n", buff );
fclose(fp);
}
void testReadWriteFile(){
//读文件
char a[]="aaaafadsafasdfsfasd";
char* c=a;
writeFile(c);
//写文件
readFile();
}
void read_write_file(){
const int MAXLEN =1024;
FILE * outfile, *infile;
outfile = fopen("./test2.jpg", "wb" );
infile = fopen("./test1.jpg", "rb");
unsigned char buf[MAXLEN];
if( outfile == NULL || infile == NULL ) {
// printf(%s, %s,argv[1],not exit\n);
printf("exit");
exit(1);
}
int rc;
while( (rc = fread(buf,sizeof(unsigned char), MAXLEN,infile)) != 0 ) {
fwrite( buf, sizeof( unsigned char ), rc, outfile );
}
fclose(infile);
fclose(outfile);
printf("read,write succes");
}
unsigned char* readImgFile(){
const int MAXLEN =1024*1000;
FILE *infile;
infile = fopen("./test1.jpg", "rb");
unsigned char buf[MAXLEN];
if(infile == NULL ) {
exit(1);
}
fread(buf,sizeof(unsigned char), MAXLEN,infile);
fclose(infile);
printf("read success");
printf("%d",sizeof(buf));
unsigned char *pBuf=buf;
printf("%d",sizeof(pBuf));
return pBuf;
}
jbyteArray as_byte_array(JNIEnv *env, unsigned char* buf, int size) {
jbyteArray array = env->NewByteArray(size);
//HERE I GET THE ERROR, I HAVE BEEN TRYING WITH len/2 and WORKS , PROBABLY SOME BYTS ARE GETTING LOST.
env->SetByteArrayRegion(array, 0, size, (jbyte*)(buf));
return array;
}
void hwCameraCallback(JNIEnv *env,jobject obj){
printf("callback begin\n");
//获取类引用
jclass thisClass = env->GetObjectClass(obj);
/*
//无参回调
jmethodID midCallBack = env->GetMethodID(thisClass, "hwCameraCallback", "()V");//获取方法id
if (NULL == midCallBack) {
printf("回调方法为空");
return;
}
env->CallVoidMethod(obj, midCallBack);//调用方法
*/
jmethodID midCallBack = env->GetMethodID(thisClass, "hwCameraCallback", "([B)V");//获取方法id
if (NULL == midCallBack) {
printf("回调方法为空");
return;
}
//方法1
unsigned char p1[]="222223232323232";
unsigned char* p2=p1;
//jbyteArray buffer=as_byte_array(env,p2,sizeof(p2));
unsigned char* p3=readImgFile();
jbyteArray buffer=as_byte_array(env,p3,1024*1000);
/*
方法2
jbyteArray buffer = env->NewByteArray(3);
jbyte *bytes = env->GetByteArrayElements( buffer, 0);
bytes[0]=1;
bytes[1]=2;
bytes[2]=3;
env->SetByteArrayRegion(buffer,0,3,bytes);
*/
env->CallVoidMethod(obj, midCallBack,buffer);//调用方法
testReadWriteFile();
read_write_file();
printf("callback end\n");
}
JNIEXPORT void JNICALL
Java_upup_ajni_CameraCtl_startHwCamera(JNIEnv *env, jobject obj)
{
printf("camera start\n");
hwCameraCallback(env,obj);
return;
}
4、运行java程序,会看到通过c代码传回的字节数组,可以生成一个图片文件。