本文承接之前的ndk开发1文章http://www.jianshu.com/p/58165ce16a72
这次的主要内容是讲java中的基本数据类型是怎么和c++文件交互的:
首先,我们扩展Leitest这个类,新增若干个native方法,这些native方法具体验证的东西是不一样的,接下来会细讲
详细代码如下
package com.example.lei.testa;
import android.content.Context;
import android.widget.Toast;
/**
* Created by lei on 17-1-11.
*/
public class Leitest {
public static int statictest=1;
private final Context appContext;
static {
System.loadLibrary("hello");
}
public Leitest (Context context){
appContext=context.getApplicationContext();
}
private void toasttest(){
Toast.makeText(appContext,"toast",Toast.LENGTH_LONG).show();
}
public String getTestvalue() {
return testvalue;
}
public void setTestvalue(String testvalue) {
this.testvalue = testvalue;
}
private String testvalue="abc";
//public native String getStringFromC();
public static native String transLateFrom(String strIn);
public static native int sumArray(int[] array);
public static native int[][]get2DArray(int size);
public native void changeFiled();
public static native void staticChange();
public native void callToast();
public native String creatNewStringByBytes(byte[]bytes);
}
第一个transLateFrom(String strIn)方法,就是将一个字符串通过c++进行大写转换,然后返回大写化的字符串,详细代码如下
JNIEXPORT jstring JNICALL Java_com_example_lei_testa_Leitest_transLateFrom
(JNIEnv * env, jclass class1, jstring obj){
const char* str= env->GetStringUTFChars(obj,NULL);
env->ReleaseStringUTFChars(obj,str);
std::string strtemp=str;
for (auto it = strtemp.begin();it!=strtemp.end() ; ++it) {
*it=towupper(*it);
}
return env->NewStringUTF(strtemp.c_str());
}
env 就是指向jni功能函数库的指针,利用 env->GetStringUTFChars(obj,NULL) 就可以将java传入的String类型(即 c++中的jstring)转换为c++里面的标准 const char* 类型的字符串,在利用env->NewStringUTF(const char*) 就可以将c++的字符串转回java的字符串(jstring)了
第二个sumArray(int[] array)函数的功能是传入一int类型的数组,然后用c++求和,返回结果,实现代码如下
JNIEXPORT jint JNICALL Java_com_example_lei_testa_Leitest_sumArray
(JNIEnv * env, jclass class1, jintArray array){
const int size= env->GetArrayLength(array);
jint *arraysum;
jint sum=0,i=0;
arraysum= env->GetIntArrayElements(array,NULL);
for ( ; i <size ; ++i) {
sum+=arraysum[i];
}
env->ReleaseIntArrayElements(array,arraysum,0);
return sum;
}
env->GetArrayLength(array) 获取传入的java数组的长度,利用env->GetIntArrayElements(array,NULL)方法得到jint(java int)型的数组(也可以认为是指向这个数组第一个元素的指针),然后遍历求和,注意为了避免内存泄露的发生,尽量释放掉内存,如env->ReleaseIntArrayElements(array,arraysum,0);
第三个 int[][]get2DArray(int size); 返回一个二维数组,主要是验证c++返回一个非基本类型的数组是怎么实现的,具体代码如下
JNIEXPORT jobjectArray JNICALL Java_com_example_lei_testa_Leitest_get2DArray
(JNIEnv * env, jclass jclass1, jint size){
jclass intarrayclass= env->FindClass("[I");
if (intarrayclass==NULL) return NULL;
jobjectArray result= env->NewObjectArray(size,intarrayclass,NULL);
if (result==NULL) return NULL;
for(int i=0;i<size;i++){
jint arryrow[size];
for (int j = 0; j < size; ++j) {
arryrow[j]=i*size+j;
}
jintArray outjarrayInt = env->NewIntArray(size);
env->SetIntArrayRegion(outjarrayInt,0,size,arryrow);
env->SetObjectArrayElement(result,i,outjarrayInt);
}
return result;
}
jclass intarrayclass= env->FindClass("[I"),JNI 函数 FindClass 来获得一个 int 型二维数组类的引用,传递给 FindClass 的参数“[I”是 JNI class descriptor(JNI 类型描述符),它对应着 java 中的 int[]类型。
然后利用env->NewObjectArray(size,intarrayclass,NULL)创建对象数组int[][],
jintArray outjarrayInt = env->NewIntArray(size) 创建临时的jint类型数组,
env->SetIntArrayRegion(outjarrayInt,0,size,arryrow) 给临时jint数组赋值,
env->SetObjectArrayElement(result,i,outjarrayInt) 将int[]做为一个元素塞入 int[][]中
第四个public native void changeFiled()和第五个 public static native void staticChange()功能上有些类似,一个是改变一个对象的成员的field的值,一个是改变一个类 static field的值,不多说,贴代码
JNIEXPORT void JNICALL Java_com_example_lei_testa_Leitest_changeFiled
(JNIEnv *env, jobject obj){
jclass testclass= env->GetObjectClass(obj);
jfieldID testfiled=env->GetFieldID(testclass,"testvalue","Ljava/lang/String;");
jstring jstring1=env->NewStringUTF("kkkk");
env->SetObjectField(obj,testfiled,jstring1);
}
JNIEXPORT void JNICALL Java_com_example_lei_testa_Leitest_staticChange
(JNIEnv * env, jclass cs){
jfieldID sfiled= env->GetStaticFieldID(cs,"statictest","I");
env->SetStaticIntField(cs,sfiled,23);
}
都是三步
1.类查找 ;
2.通过类找到field iD
3.通过fieldid修改值
需要注意的是 jfieldID testfiled=env->GetFieldID(testclass,"testvalue","Ljava/lang/String;");中的"Ljava/lang/String;"和 jfieldID sfiled= env->GetStaticFieldID(cs,"statictest","I");中的“I” 也是JNI 类型描述符,指的就是这个field的类型
第六个 public native void callToast();是用来调用类的成员函数的,代码如下
JNIEXPORT void JNICALL Java_com_example_lei_testa_Leitest_callToast
(JNIEnv *env, jobject oj){
jclass cs=env->GetObjectClass(oj);
jmethodID calltoast=env->GetMethodID(cs,"toasttest","()V");
env->CallVoidMethod(oj,calltoast);
}
同样是三步
1.类查找 ;
2.通过类找到Method iD
3.通过Method iD调用
同样注意 jmethodID calltoast=env->GetMethodID(cs,"toasttest","()V");其中“toasttest”是java中的函数名,这个见leitest的代码,"()V"也是JNI 类型描述符,表示没有参数 返回void
第七个 public native String creatNewStringByBytes(byte[]bytes)测试的是调用构造方法新建对象,功能是将byte[]转为String类型,即String的byte[]参数的构造方法测试
代码如下
JNIEXPORT jstring JNICALL Java_com_example_lei_testa_Leitest_creatNewStringByBytes
(JNIEnv * env, jobject oj, jbyteArray bytesarray){
jclass stringclass=env->FindClass("java/lang/String");
jmethodID methodId=env->GetMethodID(stringclass,"<init>","([B)V");
jobject str=env->NewObject(stringclass,methodId,bytesarray);
env->DeleteLocalRef(stringclass);
return (jstring) str;
}
补上一个返回数组的方法,被setArrayRegion坑出翔了
JNIEXPORT jintArray JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createArrayMethod
(JNIEnv *env, jobject jobj, jint len){
//1.新建长度len数组
jintArray jarr = env->NewIntArray(len);
//2.获取数组指针
jint *arr = env->GetIntArrayElements(jarr, NULL);
//3.赋值
int i = 0;
for(; i < len; i++){
arr[i] = i;
}
//4.释放资源
env->ReleaseIntArrayElements(jarr, arr, 0);
//5.返回数组
return jarr;
};
还是三步走
1.类查找 ;
2.通过类找到构造函数
3.通过NewObject调用
jmethodID methodId=env->GetMethodID(stringclass,"<init>","([B)V");中 “<init>”指构造函数,"([B)V"指参数为byte[],返回为空,JNI 类型描述符。
总结
以上代码都亲测可用,开发版本为AndroidStudio2.2,配置方案请回顾上一节的内容http://www.jianshu.com/p/58165ce16a72