在Android NDK探究奥秘二中已经列出了一个Java类型到本地映射关系 的表格。那么从C类型转化到映射的本地Java类型是怎么转化的呢?本章将给出详细答案。
基本类型转化
Java的基本类型映射到本地类型是jboolean、jbyte、jchar、jshort、jint 、jlong、jfloat、jdouble。那么我们先看看jni.h中是怎么定义的。
其中 jshort 、jint、jlong、jfloat、jdouble都是C中定义相似的。用的时候直接可以当作C中的去掉j的基本类型。jboolean、jbyte,要在C中的char类型来定义,jchar是C中的short类型定义。那么在Java基本类型映射的本地类型中和C的类型转化就非常容易了。比如在Android NDK探究奥秘一中建立的项目中。增加一个native方法:
package com.xiaoyunchengzhu.jnidemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText("数值相加为:"+sub(6,7));
}
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native int sub(int m,int n);
} 那么在C中就可以直接写
jint Java_com_xiaoyunchengzhu_jnidemo_MainActivity_sub(JNIEnv *env, jobject instance, jint m, jint n) {
int k=m+n;
return k;
}
因为jint本来就是C语言中int类型定义的类型。所以可以通用。在Jni本地代码中其它基本类型也是如此,jdouble可以直接当作double类型来用。jlong可以当作long类型来用。
jboolean、jbyte、jchar就需要当作其它各自对应C定义的类型来用了。
虽然可以这么通用,但是建议在C/C++中的运算代码还是先按照C的类型来写,最后需要和Java交互返回的数据,或者传入的数据,再转化成Java映射到本地的类型。
Java数组对象类型转化
在Android NDK探究奥秘二中也列出了一些一些Java对象类型。先看Java对象映射到本地类型:对象的转换基本上都是由JNIEnv结构里面的函数还执行的。
这些类型都是在C++中定义的,可以看出这些jobject 、jclass、jstring、jarray等归根结底都是_jobject。这些Java对象是怎么转化的呢?
jstring
首先看一下最通用的Java String类型,这个类比较特殊,单独拿出来说,在本地中是jstring,实现上面的stringFromJni方法在本地是:
jstring Java_com_xiaoyunchengzhu_jnidemo_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
return env->NewStringUTF("hello jni");
}
jstirng是由JNIEnv这个结构体来转换的 char*。也就是说是 从本地的char*转换到jstring的实现。那么从jstring转换到char*呢。比如java 代码中增加了一个native方法
public native String stringFromJNI(String value);
那么在C中value转换成C中char*来供C计算,比如字符串拼接:
jstring
Java_com_xiaoyunchengzhu_jnidemo_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */,jstring value) {
jboolean *jboolean1;
char *b="转换的值是:";
const char *a=env->GetStringUTFChars(value,jboolean1);
char *c=(char*)malloc(strlen(a)+strlen(b)+1);
strcpy(c,b);
strcat(c,a);
return env->NewStringUTF(c);
}
通过JNIEnv 调用GetStringUtfChars来转换成const char* 类型。这里是获取的utfchar还有一个函数GetStringChars.不转码的。
里面还有许多有关于jstring的函数,获取jstring的长度,获取utfstring的长度,还有ReleaseString,RealeaseS他rinUtf等。在以后的操作中都会用到。有关于分配
空间,释放空间的问题。下面是对jstring操作的函数: jstring NewString(const jchar* unicodeChars, jsize len) 这个是new一个jstring类。
jsize GetStringLength(jstring string) 获取jstring的长度。
const jchar* GetStringChars(jstring string, jboolean* isCopy) 从jstring到 const char* 的转换。
void ReleaseStringChars(jstring string, const jchar* chars) 释放 jstring
jstring NewStringUTF(const char* bytes) 这个是new一个UTF jstring类。jsize GetStringUTFLength(jstring string) 获取UTF jstring的长度const char* GetStringUTFChars(jstring string, jboolean* isCopy) 从UTF jstring到 const char* 的转换。void ReleaseStringUTFChars(jstring string, const char* utf) 释放 UTF jstring 不带 UTF的操作和带UTF的操作的转换大家都理解吧。不多说了。
Array
下面是int数组的转换例子:
在Java 代码中声明一个native方法:
public native int[] getArray(int size); 在本地实现方法为:
jintArray Java_com_xiaoyunchengzhu_jnidemo_MainActivity_getArray(JNIEnv *env, jobject instance, jint size) {
// TODO
jintArray jintArray1;
int f[size];
for (int i=0;i<size;i++){
f[i]=i;
}
jintArray1=env->NewIntArray(size);
env->SetIntArrayRegion(jintArray1,0,size,f);
return jintArray1;
}
这是一个简单的数组初始化。由 本地C的int数组进行初始化 再转为jintArray。
其它的数组也是相似:
jbooleanArray NewBooleanArray(jsize length) jbyteArray NewByteArray(jsize length) jcharArray NewCharArray(jsize length) jshortArray NewShortArray(jsize length) jintArray NewIntArray(jsize length) jlongArray NewLongArray(jsize length) jfloatArray NewFloatArray(jsize length) jdoubleArray NewDoubleArray(jsize length) 这是jobjectArray:
jobjectArray NewObjectArray(jsize length, jclass elementClass,jobject initialElement)
从数组赋值给array 为:
void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,const jboolean* buf)void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,const jbyte* buf)void SetCharArrayRegion(jcharArray array, jsize start, jsize len,const jchar* buf)void SetShortArrayRegion(jshortArray array, jsize start, jsize len,const jshort* buf)void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf)void SetLongArrayRegion(jlongArray array, jsize start, jsize len,const jlong* buf)void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,const jfloat* buf)void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble* buf) 这是jobjectArray:
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
下面是从jintArray到C int*的转换:
在Java中声明native方法:
public native int[] getAddArray(int[] m);
在本地代码中实现为:
jintArray Java_com_xiaoyunchengzhu_jnidemo_MainActivity_getAddArray(JNIEnv *env, jobject instance, jintArray m) {
jintArray jintArray1;
int *m_ = env->GetIntArrayElements(m, NULL);
int f[env->GetArrayLength(m)];
for (int i=0;i<env->GetArrayLength(m);i++){
f[i]=m_[i]+1;
}
jintArray1=env->NewIntArray(env->GetArrayLength(m));
env->SetIntArrayRegion(jintArray1,0,env->GetArrayLength(m),f);
return jintArray1;
}
其中方法的功能是发int数组每一个数据都加一。方法 GetIntArrayElements(jintArray array, jboolean* isCopy)就是jintArray到本地 int* 类型转换。这是jintArray的。
那么其它的Array也是同样道理,转换方法为:
jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy) jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy) jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy) jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy) jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy) jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
上面都是基本类型的数组,那么object类型的数组就不同了:
jobject GetObjectArrayElement(jobjectArray array, jsize index)
返回的是一个集合的一个元素。
这些操作都有释放函数,比如在上述例子:数组的每个元素加一的函数中,如果不用了,释放函数调用: env->ReleaseIntArrayElements(m, m_, 0); 这些数组的函数为:
void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems,jint mode)
void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,jint mode)void ReleaseCharArrayElements(jcharArray array, jchar* elems,jint mode)void ReleaseShortArrayElements(jshortArray array, jshort* elems,jint mode)void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode)void ReleaseLongArrayElements(jlongArray array, jlong* elems,jint mode)void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems,jint mode)void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems,jint mode)
这些就是常用的Java 基本类型和基本类型数组与 本地代码类型转换的方式。类型转化是Java和本地 交互的基础,有了这些才能互相通讯。另外,里面有关于jobject类型没有没有详细说明,是因为jobject类要拿出来单独说。Java中的类,变量 有私有变量,公有变量,保护变量,还有静态变量,非静态变量;构造方法;还有多种修饰的方法,这些在本地的转换还是比较复杂的。