博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用NDK移植开源项目,JNI的使用技巧
阅读量:5070 次
发布时间:2019-06-12

本文共 7333 字,大约阅读时间需要 24 分钟。

强烈推荐:

Android模仿易网新闻页面源码

一个简易版的iphone电子书阅读器

android仿酷狗播放器

jni 的介绍

  JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。以下介绍Android 中如何使用jni移植开源库的技巧.
  JNI日志输出到Logcat中

#include 
  #define LOG_TAG "===xcloud==="  #define LOGI(...) android_log_print(ANDROID_LOG_INFO,LOG_TAG,VA_ARGS__)  #define LOGW(...) android_log_print(ANDROID_LOG_WARN,LOG_TAG,VA_ARGS__)  #define LOGE(...) android_log_print(ANDROID_LOG_ERROR,LOG_TAG,VA_ARGS__)  Android.mk文件添加编译模块:  LOCAL_LDLIBS=-lm -llog

使用方法:

LOGE("%s",test);

JNI调用Java方法

  以调用String 的getBytes方法为例:

第一步:  jclass clsstring = (*env)->FindClass(env,"java/lang/String"); //找到Java String 类  第二步:  jstring strencode = (*env)->NewStringUTF(env,"utf-8");  //得到一个jstring 对象  第三步:  jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B"); //得到getBytes方法  第四步:  jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode); //“jstr 为传入的字符串”调用getBytes方法  第五步:  jsize alen = (*env)->GetArrayLength(env,barr); //得到数组长度  jstring 转换 char*  char* test=(char*)(*env)->GetStringUTFChars(env,jstringVariable,NULL);

返回一个java对象数组

第一步:  jclass objectClass=(*env)->FindClass(env,"com/xuzhitech/xcloud/resource"); //找到对应的java 类(对象)  第二步:  jobjectArray array=(*env)->NewObjectArray(env,currentListCount,objectClass,NULL); //通过取到的java类(对象)创建一个指定固定大小的数组  第三步:  jfieldID _uri=(*env)->GetFieldID(env,objectClass,"uri","Ljava/lang/String;");//找到对象中的列  注意:在JNI中并未提供jstring 类型的对象,所以必须通过L指定包名找到该类,在有提供的类型中,可以直接使用该类型的大写首字母(jlong 需使用J)  如jint 类型可以如此编写:  jfieldID _vcr=(*env)->GetFieldID(env,objectClass,"is_vcr","I");对应的JNI提供类型  第四步:  jmethodID jid = (*env)->GetMethodID(env,objectClass,"
","()V");//"
"代表可以访问默认构造函数  jobject jobj=(*env)->NewObject(env,objectClass,jid); //通过第一步找到的jclass创建对应的对象  第五步:  (*env)->SetObjectField(env,jobj,_modtime,jmodtime); //为对象中的每一列赋值。注意:如果JNI有提供的数据类型,可按提供的类型为对象中的列赋值,  如:(*env)->SetIntField(env,jobj,_vcr,current->is_vcr);  第六步:  (*env)->SetObjectArrayElement(env,array,i,jobj);将对象设置进第二步声明的数组中  DeleteLocalRef(env,jobj);//删除引用对象

在JNI中循环读取对象数组

for (i = 0 ;i < currentListCount ;i++) {jobject obj = (*env)->GetObjectArrayElement(env,array,i);jstring jstr=(*env)->GetObjectField(env,obj,_uri);LOGE("=====uri===%s====",jstringtoChar(env,jstr));}上面使用到的jstringtoChar方法代码:/***jstring to char*/char* jstringtoChar(JNIEnv* env,jstring jstr){char* rtn = NULL;jclass clsstring = (*env)->FindClass(env,"java/lang/String");jstring strencode = (*env)->NewStringUTF(env,"utf-8");jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode);jsize alen = (*env)->GetArrayLength(env,barr);jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);if (alen > 0){rtn = (char*)malloc(alen + 1);memcpy(rtn, ba, alen);rtn[alen] = 0;}(*env)->ReleaseByteArrayElements(env,barr, ba, 0);return rtn;

cpp JNI与 c JNI的主要注意事项:

用C编写jni文件,访问JNI内置提供方法格式:

  (*env)->SetObjectArrayElement(env,array,i,jobj);
  用CPP编写JNI文件,访问JNI内置提供方法格式:
  env->SetObjectArrayElement(array,i,jobj);
  另外,使用CPP编写的JNI代码,在调用C语言编写的库的时候,要添加以下代码,才可以正常使用(不然在链接的时候找不到相关接口:undefined reference.....):

ifdef __cplusplus  extern "C" {  #endif  ... //引入的头文件  #ifdef __cplusplus  }  #endif

其他用法几乎一致。

JNI 使用Native注册
  按照上面的方法写函数体,必须遵循JNI官方的一大堆标准进行方法的定义,有时候方法一多,不大好管理,也不利用查看,并且每次都要写一大堆恶心的标准方法名也不是一件好事。对此JNI有一套可以通过Native 注册的机制可以使用,以方便函数体的编写。
  以下是Android 调用JNI注册Natives 的步骤:

第一步  声明Java中要调用jni 的类路径:  static const char *className="com/xuzhitech/xcloud/cadaver";  第二步  创建方法格式结构体:  struct JNINativeMethod {  const char* name;//method name  const char* signature; //java method return value  void* fnPtr;//c/c++ method  } ;  第三步  使用结构体注册需要供Android 调用的方法体:
static JNINativeMethod methods[] = {{
"StringTestOne", "()Ljava/lang/String;", (void*)StringTestOne},{
"executels","()[Lcom/xuzhitech/xcloud/resource;",(void*)executels},{
"getProgress","()Lcom/xuzhitech/xcloud/fileProgress;",(void*)getProgress},{
"getdownloadState","()I",(void*)getdownloadState},{
"changeExecutable","(Ljava/lang/String;Ljava/lang/String;)I",(void*)changeExecutable},{
"Login","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",(void*)Login},{
"getCurrentListCount","()I",(void*)getCurrentListCount},{
"mkcol","(Ljava/lang/String;)I",(void*)mkcol},{
"deleteFile","(Ljava/lang/String;)I",(void*)deleteFile},{
"deleteCol","(Ljava/lang/String;)I",(void*)deleteCol},{
"getFullUri","()Ljava/lang/String;",(void*)getFullUri},{
"cdCommand","(Ljava/lang/String;)I",(void*)cdCommand},{
"logout","()V",(void*)logout},{
"doCopy","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",(void*)doCopy},{
"doMove","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",(void*)doMove},{
"getFile","(Ljava/lang/String;Ljava/lang/String;)V",(void*)getFile},{
"putFile","(Ljava/lang/String;Ljava/lang/String;)V",(void*)putFile},{
"lock","(Ljava/lang/String;)I",(void*)lock},{
"unlock","(Ljava/lang/String;Ljava/lang/String;)I",(void*)unlock},{
"getToken","(Ljava/lang/String;)Ljava/lang/String;",(void*)getToken},{
"isSetToken","(Ljava/lang/String;)I",(void*)isSetToken},

第一个参数为:Java实现要调用的方法名称

  第二个参数为:该方法需要的返回值与方法参数,如->(方法参数)返回值,详细使用参考如下:
  具体的每一个字符的对应关系如下
  字符 Java类型 C类型

V void voidZ jboolean booleanI jint intJ jlong longD jdouble doubleF jfloat floatB jbyte byteC jchar charS jshort short

数组则以"["开始,用两个字符表示

[I jintArray int[][F jfloatArray float[][B jbyteArray byte[][C jcharArray char[][S jshortArray short[][D jdoubleArray double[][J jlongArray long[][Z jbooleanArray boolean[]

也可返回任意java对象,如上代码的()[Lcom/xuzhitech/xcloud/resource;代表一个不带参数的方法,并且返回java类中的Lcom/xuzhitech/xcloud/resource;数组。'['代表数组的意思,'L'代表类的意思

  第三个参数为,第一个参数需要映射的本地c/c++对应的函数指针方法。
  第四步
  在加载jni的时候指定JNI版本并且通过传入进来的class路径注册Natives 方法

 

jint JNI_OnLoad(JavaVM* vm, void* reserved){ jint result = JNI_ERR;JNIEnv* env = NULL;jclass clazz;int methodsLenght;if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {LOGE("ERROR: GetEnv failed\n");return JNI_ERR;}// assert(env != NULL); clazz = (*env)->FindClass(env,className);if (clazz == NULL) {LOGE("Native registration unable to find class '%s'", className);return JNI_ERR;}methodsLenght = sizeof(methods) / sizeof(methods[0]);if ((*env)->RegisterNatives(env,clazz, methods, methodsLenght) < 0) {LOGE("RegisterNatives failed for '%s'", className);return JNI_ERR;}// result = JNI_VERSION_1_4;return result;}

注意,使用Natives注册运行程序时,它会先检测jni与java类使用jni 类的native 方法是否相对应与一致,即使你没有使用到该 方法也会进行检测。

  第五步
  通过上面的修改,c/c++编写的jni 文件就可以不带一大串标准名称了,您可以像正常编写c一样编写你的jni函数,如下:

 /**move file*/jint doMove(JNIEnv* env,jobject thiz,jstring jsrc,jstring jdest,jstring jrename){char* src=(char*)(*env)->GetStringUTFChars(env,jsrc,NULL);char* dest=(char*)(*env)->GetStringUTFChars(env,jdest,NULL);char* rename=(char*)(*env)->GetStringUTFChars(env,jrename,NULL);int returnValue=multi_move(src,dest,rename);// free(src);(*env)->ReleaseStringUTFChars(env,jsrc,src);// free(dest);(*env)->ReleaseStringUTFChars(env,jdest,dest);// free(rename);(*env)->ReleaseStringUTFChars(env,jrename,rename);return returnValue;}

 

 

转载于:https://www.cnblogs.com/vus520/archive/2012/08/09/2630243.html

你可能感兴趣的文章
模板统计LA 4670 Dominating Patterns
查看>>
团队项目开发客户端——登录子系统的设计
查看>>
IO—》Properties类&序列化流与反序列化流
查看>>
session如何保存在专门的StateServer服务器中
查看>>
react展示数据
查看>>
测试计划
查看>>
选择器
查看>>
Mysql与Oracle 的对比
查看>>
jquery实现限制textarea输入字数
查看>>
thinkphp5 csv格式导入导出(多数据处理)
查看>>
PHP上传RAR压缩包并解压目录
查看>>
Codeforces 719B Anatoly and Cockroaches
查看>>
jenkins常用插件汇总
查看>>
c# 泛型+反射
查看>>
第九章 前后查找
查看>>
Python学习资料
查看>>
多服务器操作利器 - Polysh
查看>>
[LeetCode] Candy
查看>>
jQuery 自定义函数
查看>>
jquery datagrid 后台获取datatable处理成正确的json字符串
查看>>