java_jni详解_02

17 篇文章 0 订阅
直接看代码吧

public class Prompt{

public native String getLine(String prompt); //声明本地方法

public static void main(String[] args) {
Prompt p = new Prompt();
String input = p.getLine("Type a line:");
// System.out.println(System.getProperty("java.library.path"));
System.out.println("User typed: " + input);
}

static{
//System.loadLibrary("helloworld"); // 加载库文件
System.load("/Users/zhaoshun/solibs/libhelloStr.so");
}
}

/**
*
* 头文件中的方法原型
* Class: jni_sample_Prompt
* Method: getLine
* Signature: (Ljava/lang/String;)Ljava/lang/String;
* JNIEXPORT jstring JNICALL Java_jni_sample_Prompt_getLine
* (JNIEnv *, jobject, jstring);
*
* Java_Prompt_getLine接收3个参数: JNIEnv结构包括JNI函数表。
* 第二个参数的意义取决于该方法是静态还是实例方法(static or an instance method)。
* 当本地方法作为一个实例方法时,第二个参数相当于对象本身,即this. 当本地方法作为 一个(static类型的方法)静态方法时,指向所在类.
* 在本例中,Java_Prompt_getLine是一个本地实例方法实现, 所以jobject 指向对象本身。
* Java Native(jni.h)
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble

相比基本类型,对象类型的传递要复杂很多。 Java层对象作为opaque references(指 针)传递到JNI层。
Opaque references是一种C的指针类型,它指向JavaVM内部数据结构。 使用这种指针的目的是:不希望JNI用户了解JavaVM内部数据结构。
对Opaque reference 所指结构的操作,都要通过JNI方法进行. 比如,"java.lang.String"对象,JNI层对应的 类型为jstring,
对该opaque reference的操作要通过JNIEnv->GetStringUTFChars进行。

一定要按这种原则编程,千万不要为了效率或容易的取到某个值,绕过 JNI,直接操作 opaque reference.
JNI 是一套完善接口,所有需求都能满足。
在JNI中对象的基类即为jobject. 为方便起见,还定义了jstring,jclass, jobjectArray 等结构,他们都继承自 jobject。

使用对应的 JNI 函数把 jstring 转成 C/C++字串。JNI 支持 Unicode/UTF-8 字符编码互转。
Unicode 以 16-bits 值编码;UTF-8 是一种以字节为单位变长格式的字符编码,并与 7-bits ASCII码兼容。
UTF-8字串与C字串一样,以NULL('\0')做结束符, 当UTF-8包含非ASCII 码字符时,以'\0'做结束符的规则不变。
7-bit ASCII字符的取值范围在1-127之间,这些 字符的值域与 UTF-8 中相同。当最高位被设置时,表示多字节编码。
如下,调用 GetStringUTFChars,把一个 Unicode 字串转成 UTF-8 格式字串,如果你确定 字串只包含7-bit ASCII字符。
这个字串可以使用C库中的相关函数,如printf.
*
*/


#include <jni.h>
#include <stdio.h>
#include "Prompt.h"
// printf 在stdio.h头文件中
JNIEXPORT jstring JNICALL Java_jni_sample_Prompt_getLine (JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const jbyte *str;
str = (*env)->GetStringUTFChars(env,prompt,NULL);
if(str == NULL){
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s",str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%127s",buf);
return (*env)->NewStringUTF(env, buf);
}

/***
记得检测 GetStringUTFChars 的返回值,因为调用该函数会有内存分配操作,失败后,该 函数返回 NULL,并抛 OutOfMemoryError 异常。
如何处理异常,后面会有介绍。JNI 处理异常,不同于 Java 中的 try...catch。在 JNI 中, 发生异常,不会改变代码执行轨迹,所以,当返回 NULL,要及时返回,或马上处理异常。

调用ReleaseStringUTFChars释放GetStringUTFChars中分配的内存(Unicode -> UTF-8 转换的原因)。
使用 JNIEnv->NewStringUTF 构造 java.lang.String;如果此时没有足够的内存, NewStringUTF 将抛 OutOfMemoryError 异常,同时返回 NULL。

除了GetStringUTFChars, ReleaseStringUTFChars, 和NewStringUTF, JNI还支持其他 操作 String 的函数供使用。
GetStringChars 是有 Java 内部 Unicode 到本地 UTF-8 的转换函数,可以调用 GetStringLength,获得以 Unicode 编码的字串长度。也可以使用 strlen 计算 GetStringUTFChars 的返回值,得到字串长度。
const jchar * GetStringChars(JNIEnv *env, jstring str, jboolean *isCopy);

上述声明中,有 isCopy 参数,当该值为 JNI_TRUE,将返回 str 的一个拷贝;为 JNI_FALSE将直接指向str的内容。 注意:当isCopy为JNI_FALSE,不要修改返回值,不 然将改变 java.lang.String 的不可变语义。
一般会把isCopy设为NULL,不关心Java VM对返回的指针是否直接指向 java.lang.String 的内容。

记住在调用 GetStringChars 之后,要调用 ReleaseStringChars 做释放,不管在调用 GetStringChars 时为 isCopy 赋值 JNI_TRUE 还是 JNI_FALSE,因不同 JavaVM 实现的原因, ReleaseStringChars 可能释放内存,也可能释放一个内存占用标记(isCopy 参数的作用,从 GetStringChars 返回一个指针,该指针直接指向 String 的内容,为了避免该指针指向的内 容被 GC,要对该内存做锁定标记)。

JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) {
char outbuf[128], inbuf[128];
int len = (*env)->GetStringLength(env, prompt); (*env)->GetStringUTFRegion(env, prompt, 0, len, outbuf); printf("%s", outbuf);
scanf("%s", inbuf);
return (*env)->NewStringUTF(env, inbuf);
}
GetStringUTFRegion有两个参数,starting index 和 length, 这两个参数以Unicode编 码计算. 该函数会做边界检查,所以可能抛出StringIndexOutOfBoundsException。
因为该函数不涉及内存操作,所以较 GetStringUTFChars 使用要简单。

有两个函数:GetStringLength/GetStringUTFLength,前者是 Unicode 编码长度,后者 是 UTF 编码长度。
GetStringUTFRegion 很有用,因为你不能修改 GetStringUTFChars 返回值,所以需要另 外 malloc/strcpy 之后,再操作返回值,耗时费力,不如直接使用 GetStringUTFRegion 来 的简洁、高效。
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值