首页 > 其他分享 >使用NDK移植开源项目,JNI的使用技…

使用NDK移植开源项目,JNI的使用技…

时间:2023-07-28 18:32:01浏览次数:44  
标签:lang Ljava NDK String void 开源 env JNI


jni 的介绍

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

JNI日志输出到Logcat中

#include <android/log.h>
  
 #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方法为例:

jstring 转换 char*

第一步:
  
 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); //得到数组长度


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提供类型可以参考如下 

http://download.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html


第四步:
  
 jmethodID jid = (*env)->GetMethodID(env,objectClass,"<init>","()V");//"<init>"代表可以访问默认构造函数
  
 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方法代码:
    

 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类型


 

 
   
void 
    void
    
 Z 
    jboolean 
    boolean
    
 I 
    jint 
    int
    
 J 
    jlong 
    long
    
 D 
    jdouble 
    double
    
 F 
    jfloat 
    float
    
 B 
    jbyte 
    byte
    
 C 
    jchar 
    char
    
 S 
    jshort 
    short



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


 

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

使用NDK移植开源项目,JNI的使用技…_java

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



第三个参数为,第一个参数需要映射的本地c/c++对应的函数指针方法。

第四步
在加载jni的时候指定JNI版本并且通过传入进来的class路径注册Natives 方法




使用NDK移植开源项目,JNI的使用技…_java


JNI_OnLoad(JavaVM* vm, 
    void
    * reserved){ 
    
 
   
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函数,如下:



使用NDK移植开源项目,JNI的使用技…_java

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;
    
 }
    
   
  

注:

JNI的一些基本方法的使用都可以参考这个网站:http://docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html

标签:lang,Ljava,NDK,String,void,开源,env,JNI
From: https://blog.51cto.com/u_548275/6886110

相关文章

  • 比JDK最高快170倍,蚂蚁开源一款序列化框架!
    点击“终码一生”,关注,置顶公众号每日技术干货,第一时间送达! Fury是一个基于JIT动态编译和零拷贝的多语言序列化框架,支持Java/Python/Golang/JavaScript/C++等语言,提供全自动的对象多语言/跨语言序列化能力,和相比JDK最高170倍的性能。GitHub地址为:https://github.com/al......
  • AICodeConvert 开源了,从 6.24 到现在一个月,没有主动推广,居然有9.8K 访问量
    这是我另个一个之前周六6.24开始验证思路的项目,验证的感觉差不多,不做主动推广到现在一个月,访问量9.8K。源码开源了,github.com网址:AICodeConvert另一个在佛系验证中的还有这个:Base64.kr,思路见 github启发来源:有两个1.社群聊过的producthunt提交AI作品,那么就静静的等......
  • Game as a Service —— 开源云游戏搭载WebRTC
    软件即服务,基础架构即服务,平台即服务,通信平台即服务,视频会议即服务,那么,游戏即服务(GameasaService)如何呢?已经有不少科技公司试水云游戏,最著名的要数Google的Stadia。对WebRTC来说,Stadia已经算是老朋友了,但是其他云游戏也能以同样的方式运用WebRTC吗?ThanhNguyen研究了他自己的开......
  • 在 Linux 平台下使用 JNI
     引言Java的出现给大家开发带来的极大的方便。但是,如果我们有大量原有的经过广泛测试的非Java代码,将它们全部用Java来重写,恐怕会带来巨大的工作量和长期的测试;如果我们的应用中需要访问到特定的设备,甚至是仅符合公司内部信息交互规范的设备,或某个特定的操作系统才有的特性,Jav......
  • 开源快速开发平台:做好数据管理,实现流程化办公!
    做好数据管理,可以提升企业的办公协作效率,实现数字化转型。开源快速开发平台是深受企业喜爱的低代码开发平台,拥有多项典型功能,是可以打造自主可控快速开发平台,实现一对一框架定制的软件平台。在快节奏的社会中,开源快速开发平台依托优势特点深得客户喜爱和支持。了解了开源快速开发......
  • Java JNI(Java Native Interface)介绍
    1.JNI简介众所周知,Java的主要优势之一是它的可移植性,这意味着一旦我们编写并且编译了代码,这个过程的结果就是不依赖于平台的字节码。它可以像我们预期的那样运行在任何能够运行Java虚拟机的机器或设备上。 但是,有时我们确实需要使用一些为某些特定架构而进行本地编译的原......
  • 开源视频监控管理平台国标GB28181视频EasyCVR电子地图功能展示优化
    视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。视频监控综合管理平台EasyCVR可提供的视频能力包括:视频监控直播、云端录像、云存储、录像检索与回......
  • 叮~OpenSCA社区拍了拍您并发来一份开源盛会邀请函
    2023数字供应链安全大会(DSS2023)将于8月10日在北京·国家会议中心举办。本次大会以“开源的力量”为主题,由悬镜安全主办,ISC互联网安全大会组委会、中国软件评测中心(工业和信息化部软件与集成电路促进中心)、中国信息通信研究院云计算与大数据研究所、CCF计算机安全专业委会联合发起......
  • Android开源防火墙软件droidwall
    给大家推荐一个开源的防火墙项目,感兴趣的朋友可以研究一下。DroidWall是Android操作系统上一款强大的网络防火墙,软件原理是利用linux中iptables,根据创建iptables规则,阻止某些应用程序进行访问网络,屏蔽软件中垃圾广告。项目地址:https://code.google.com/p/droidwall/源代码下载地......
  • 【软件记录】开源软件 #1
    1.Calibre-电子书管理源码: https://github.com/kovidgoyal/calibre官网: https://www.calibre-ebook.com/zh_CN 2.SumatraPDF-PDF阅读器源码: https://github.com/sumatrapdfreader/sumatrapdf官网: https://www.sumatrapdfreader.org/free-pdf-reader 3.sK1......