本地代码中使用Java对象
通过使用合适的JNI函数,你可以创建Java对象,get、set 静态(static)和 实例(instance)的域,调用静态(static)和实例(instance)函数。JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。
下表列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数。每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对应返回的jfieldID或jmethodID。
函数 | 描述 |
GetFieldID | 得到一个实例的域的ID |
GetStaticFieldID | 得到一个静态的域的ID |
GetMethodID | 得到一个实例的方法的ID |
GetStaticMethodID | 得到一个静态方法的ID |
构造一个Java对象的实例
jclass cls = (*env)->FindClass(env, "Lpackagename/classname;"); //创建一个class的引用 jmethodID id = (*env)->GetMethodID(env, cls, "", "(D)V"); //注意这里方法的名称是"",它表示这是一个构造函数,而且构造参数是double型的 jobject obj = (*env)->NewObjectA(env, cls, id, args); //获得一实例,args是构造函数的参数,它是一个jvalue*类型。
首先是获得一个Java类的class引用 (*env)->FindClass(env, "Lpackagename/classname;"); 请注意参数:Lpackagename/classname; ,L代表这是在描述一个对象类型,packagename/classname是该对象耳朵class路径,请注意一定要以分号(;)结束!
然后是获取函数的id,jmethodID id = env->GetMethodID(cls, "", "(D)V"); 第一个是刚刚获得的class引用,第二个是方法的名称,最后一个就是方法的签名了
还是不懂?我曾经如此,请接着看...
难理解的函数签名
JNINativeMethod的定义如下:
typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针,指向C函数。
其中比较难以理解的是第二个参数,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
那其他情况呢?请查看下表:
类型
|
符号 |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
object对象 | LClassName; L类名; |
Arrays |
[array-type [数组类型
|
methods方法 | (argument-types)return-type (参数类型)返回类型 |
稍稍补充一下:
1、方法参数或者返回值为java中的对象时,签名中必须以“L”加上其路径,不过此路径必须以“/”分开,自定义的对象也使用本规则
比如说 java.lang.String为“java/lang/String”,com.nedu.jni.helloword.Student为"Lcom /nedu/jni/helloword/Student;"
2、方法参数或者返回值为数组类型时,请前加上[
例如[I表示 int[],[[[D表示 double[][][],即几维数组就加几个[
在本地方法中调用Java对象的方法
1、获取你需要访问的Java对象的类:
jclass cls = (*env)->GetObjectClass(env, obj); // 使用GetObjectClass方法获取obj对应的jclass。 jclass cls = (*env)->FindClass(“android/util/log”) // 直接搜索类名,需要是static修饰的类。
2、获取MethodID:
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); //GetStaticMethodID(…),获取静态方法的ID使用GetMethdoID方法获取你要使用的方法的MethdoID
其参数的意义:
env-->JNIEnv
cls-->第一步获取的jclass
"callback"-->要调用的方法名
"(I)V"-->方法的Signature, 签名同前面的JNI规则。
3、调用方法:
(*env)->CallVoidMethod(env, obj, mid, depth);// CallStaticIntMethod(….) , 调用静态方法
使用CallVoidMethod方法调用方法。参数的意义:
env-->JNIEnv
obj-->通过本地方法穿过来的jobject
mid-->要调用的MethodID(即第二步获得的MethodID)
depth-->方法需要的参数(对应方法的需求,添加相应的参数)
注:这里使用的是CallVoidMethod方法调用,因为没有返回值,如果有返回值的话使用对应的方法,在后面会提到。
CallVoidMethod CallStaticVoidMethod CallIntMethod CallStaticVoidMethod CallBooleanMethod CallStaticVoidMethod CallByteMethod CallStaticVoidMethod
现在稍稍明白文章开始构造Java对象那个实例了吧?让我们继续深入一下:
Jni操作Java的String对象
从java程序中传过去的String对象在本地方法中对应的是jstring类型,jstring类型和c中的char*不同,所以如果你直接当做char*使用的话,就会出错。因此在使用之前需要将jstring转换成为c/c++中的char*,这里使用JNIEnv提供的方法转换。
const char *str = (*env)->GetStringUTFChars(env, jstr, 0); (*env)->ReleaseStringUTFChars(env, jstr, str);
这里使用GetStringUTFChars方法将传进来的prompt(jstring类型)转换成为UTF-8的格式,就能够在本地方法中使用了。
注意:在使用完你所转换之后的对象之后,需要显示调用ReleaseStringUTFChars方法,让JVM释放转换成UTF-8的string的对象的空间,如果不显示的调用的话,JVM中会一直保存该对象,不会被垃圾回收器回收,因此就会导致内存溢出。
下面是Jni访问String对象的一些方法:
- GetStringUTFChars 将jstring转换成为UTF-8格式的char*
- GetStringChars 将jstring转换成为Unicode格式的char*
- ReleaseStringUTFChars 释放指向UTF-8格式的char*的指针
- ReleaseStringChars 释放指向Unicode格式的char*的指针
- NewStringUTF 创建一个UTF-8格式的String对象
- NewString 创建一个Unicode格式的String对象
- GetStringUTFLength 获取UTF-8格式的char*的长度
- GetStringLength 获取Unicode格式的char*的长度
下面提供两个String对象和char*互转的方法:
/* c/c++ string turn to java jstring */ jstring charToJstring(JNIEnv* env, const char* pat) { jclass strClass = (*env)->FindClass(env, "java/lang/String"); jmethodID ctorID = (*env)->GetMethodID(env, strClass, "", "([BLjava/lang/String;)V"); jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat)); (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = (*env)->NewStringUTF(env, "UTF-8"); return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding); } /* java jstring turn to c/c++ char* */ char* jstringToChar(JNIEnv* env, jstring jstr) { char* pStr = NULL; jclass jstrObj = (*env)->FindClass(env, "java/lang/String"); jstring encode = (*env)->NewStringUTF(env, "utf-8"); jmethodID methodId = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode); jsize strLen = (*env)->GetArrayLength(env, byteArray); jbyte *jBuf = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE); if (jBuf > 0) { pStr = (char*)malloc(strLen + 1); if (!pStr) { return NULL; } memcpy(pStr, jBuf, strLen); pStr[strLen] = 0; } env->ReleaseByteArrayElements(byteArray, jBuf, 0); return pStr; }
本文转自Zhiweiofli's Blog,转载请注明出处,谢谢。
相关推荐
在Android开发中,NDK(Native ...总之,NDK开发中的C/C++调用Java方法是一项关键技能,它能让你充分利用原生代码的优势,同时解决Java层难以处理的问题。理解和熟练运用JNI,将使你的Android应用性能更上一层楼。
NDK编译和JAVA JNI原生调用是Android开发中的高级技术,主要应用于游戏开发、音视频处理、图像识别等对性能要求较高的场景。 1. NDK简介: NDK提供了一套工具,让开发者可以在Android应用中使用本地代码。它包含了...
在Android开发中,NDK(Native Development Kit)和JNI(Java Native Interface)是两个关键工具,它们允许开发者使用C++或者其他本地语言编写部分代码,从而提高应用的性能或实现特定功能。本文将深入探讨Android ...
Android NDK 提供了一個工具 javah,可以将 Java 代码中的 JNI 方法转换为 C/C++ 头文件,以便进行 JNI 的 C/C++ 端程序的开发。 javah 命令是 Android NDK 中的一个重要工具,主要用于将 Java 代码中的 JNI 方法...
总结来说,"Android使用JNI调用Python so解释器"涉及到Android NDK开发,JNI接口设计,Python解释器的Android移植,以及跨语言通信等多个技术层面。这种技术虽然复杂,但能充分利用Python的灵活性和Android的广泛...
JNI(Java Native Interface)是Java平台的标准本地接口,它使得Java代码可以调用本地(非Java)代码,同时也让本地代码能够调用Java对象。在Android应用开发中,NDK和JNI结合使用,通常用于实现计算密集型任务、...
在Android中,NDK(Native Development Kit)提供了工具和库来支持JNI开发。使用NDK时,通常需要配置Android.mk或CMakeLists.txt文件来编译原生代码,并将其打包到APK中。在运行时,Android系统会自动加载对应的本地...
Android JNI(Java Native Interface)是Android系统提供的一种机制,它允许Java代码调用C/C++原生代码,同时也允许C/C++代码调用Java的方法。JNI在开发高性能、低级硬件交互、使用现有C库或者优化性能的关键部分时...
本文将深入探讨在Android中如何使用JNI,特别是如何从C++ native代码调用Java层的类、方法、属性以及接口,同时涉及线程回调接口的实现。 首先,我们需要了解JNI的基本结构。一个典型的JNI应用会包含Java源文件、C/...
2. **JNI函数**:JNI包含了一系列的函数,如`FindClass`用于查找Java类,`GetMethodID`获取Java方法的ID,`NewObject`创建Java对象,`CallVoidMethod`、`CallIntMethod`等调用Java方法。这些函数让Java和本地代码...
在这个文件中,我们需要实现JNI函数,通过JNI函数调用Java接口的方法。首先,使用`JNIEnv*`指针获取到Java对象,然后调用Java接口的方法: ```cpp #include <jni.h> #include extern "C" JNIEXPORT void ...
在Android应用运行时,Java层可以通过JNI调用这些C++函数进行AES加解密。 **安全注意事项** 在实际应用中,确保正确管理密钥是非常重要的。密钥不应硬编码在代码中,应存储在安全的位置,如Android Keystore系统。...
2. **JNI调用脚本**: JNI是Java和本地代码(如C/C++)之间的桥梁。它定义了一组接口,使得Java可以调用C/C++函数,反之亦然。在Android中,JNI接口通常用于扩展Android应用的功能,或者利用C/C++库来提高性能。 3. ...
Android 通过 JNI(Java Native Interface)调用.so 动态库是 Android 开发中的一种常用技术。JNI 是一种允许 Java 代码与 native 代码之间进行交互的接口。通过 JNI,我们可以在 Android 项目中调用.so 动态库中的 ...
本篇文章将深入探讨NDK开发中的一个重要话题:如何从C语言层面调用Java方法以及获取Java对象的属性。 首先,我们需要理解JNI(Java Native Interface),它是Java平台的标准本地接口,用于在Java代码和本地(非Java...
安卓app开发之NDK入门教程,JAVA代码通过JNI接口调用NDK代码(C语言编写的linux android功能).zip
本文将深入探讨如何利用Android NDK(Native Development Kit)在应用程序中使用JNI调用驱动程序。 首先,我们需要了解NDK的基本概念。NDK是Google提供的一套工具集,用于在Android平台上编写和编译原生代码。它...
接着,“Android NDK(r5b)环境搭建及JNI实现.docx”文件着重于JNI(Java Native Interface)的使用,这是Android应用与NDK之间通信的关键桥梁。JNI允许Java代码调用C/C++函数,反之亦然。开发者需要理解JNI的接口...
3. Android Native Development Kit (NDK):虽然这里不是在开发Android应用,但NDK包含了一些必要的JNI工具,如`javah`,用于生成C++的JNI接口声明。 接下来,我们按照以下步骤操作: **步骤1:创建Java类** 编写...