前面介绍了JNI的基本规范以及JNI的接口的生成过程。本文通过一个jni_test 应用实践操作JNI的接口各种典型应用。
jni_test从jni的字符串,数组,基本类型,自定义类,C回调java静态方法,C回调Java的实例方法,C访问Java的静态变量,C访问Java的实例变量,Java传内存数据给C,C传内存数据给Java 共10个demo演示jni各种典型应用场景。
一、UI设计
通过修改active_main.xml(为了简单起见采用相对布局),或者直接使用design工具拖动布局(先拖一个layout线下布局,再在该布局上排上控件,类似MFC的UI设计原理),UI效果如下。
jni_test的UI界面
二、JIN接口的定义
Java JNI接口声明如下:
public native String stringFromJNI(String src);
public native String CallbackFuncTest();
public native int CallbackClassTest();
public native int CallbackStaticVarTest();
public native int CallbackVarTest(TestNative obj);
public native int add(int x,int y);
public native int [] updateArray(int [] src,int len);
public native void passmem2C(int [] src,int len);
public native int [] getmem2C();
三、在native层jni实现如下:
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jni_1test_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */,
jstring src) {
std::string hello = "Hello3 from C++";
const char* native_string = env->GetStringUTFChars(src, NULL);
std::string temp(native_string);
std::string dst = hello+temp;
env->ReleaseStringUTFChars(src, native_string);
return env->NewStringUTF(dst.c_str());
}
//回调java 静态的方法
extern "C" JNIEXPORT jstring Java_com_example_jni_1test_MainActivity_CallbackFuncTest(
JNIEnv* env,
jobject /* this */ ){
jclass mJclass;
jstring mJstring;
jmethodID mJStaticmethodID;
//1.从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
int i = 0;
mJclass = env->FindClass("com/example/jni_test/TestNative"); //JNIEnv*, const char*
if (mJclass == NULL) {
LOGI("test","callJavaStaticMethod==>>mJclass==NULL==>>%s", "");
return env->NewStringUTF("CallbackTest is failed");
}
// 2、从clazz类中查找callStaticMethod方法
mJStaticmethodID = env->GetStaticMethodID(mJclass,
"settext",
"(Ljava/lang/String;)V"); //JNIEnv*, jclass, const char*, const char*
if (mJStaticmethodID == NULL) {
LOGI("test","=====>>>can not foud callStaticMethod");
return env->NewStringUTF("CallbackFuncTest is failed");
}
mJstring= env->NewStringUTF("settext is now");
env->CallStaticVoidMethod(mJclass, mJStaticmethodID, mJstring);
// 删除局部引用
env->DeleteLocalRef(mJstring);
env->DeleteLocalRef(mJclass);
return env->NewStringUTF("CallbackFuncTest is now");
}
//回调java中的类
extern "C" JNIEXPORT jint Java_com_example_jni_1test_MainActivity_CallbackClassTest(
JNIEnv* env,
jobject /* this */ ) {
jclass mJclass = NULL;
jmethodID jmethodID_Construct; //构造方法ID
jmethodID jmethodID_Method_Instance; //方法的ID
jmethodID jmethodID_Method_Instance2; //方法的ID
jobject jobjectMyClass; //类的实例
//1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
mJclass = env->FindClass("com/example/jni_test/TestNative");
if (mJclass == NULL) {
printf("====FindClass not found \n");
return 0;
}
// 2、获取类的默认构造方法ID
jmethodID_Construct = env->GetMethodID(mJclass, "<init>", "(I)V");
if (jmethodID_Construct == NULL) {
printf("GetMethodID not found ==>>jmethodID_Construct");
return 0;
}
// 3、查找实例方法的ID
jmethodID_Method_Instance = env->GetMethodID(mJclass,
"setnum", "(I)V");
if (jmethodID_Method_Instance == NULL) {
return 0;
}
// 4、创建该类的实例 调用无参构造方法 如果其它构造方法,后面可传参数
jobjectMyClass = env->NewObject(mJclass, jmethodID_Construct,100); //JNIEnv*, jclass, jmethodID, ...
if (jobjectMyClass == NULL) {
return 0;
}
// 5、调用对象的实例方法
env->CallVoidMethod(jobjectMyClass, jmethodID_Method_Instance,
100); //JNIEnv*, jobject, jmethodID, ...
// 3、查找实例方法的ID
jmethodID_Method_Instance2 = env->GetMethodID(mJclass,
"getnum", "()I");
if (jmethodID_Method_Instance2 == NULL) {
return 0;
}
int ret;
ret=env->CallIntMethod(jobjectMyClass, jmethodID_Method_Instance2); //JNIEnv*, jobject, jmethodID, ...
// 删除局部引用
env->DeleteLocalRef(jobjectMyClass);
return ret;
}
//回调java中的类中静态变量
extern "C" JNIEXPORT jint Java_com_example_jni_1test_MainActivity_CallbackStaticVarTest(
JNIEnv* env,
jobject /* this */ ) {
jclass clazz;
jfieldID fid;
int test;
//1.获取ClassField类的Class引用
clazz = env->FindClass("com/example/jni_test/TestNative");
if (clazz == NULL) { // 错误处理
return 0;
}
//2.获取ClassField类静态变量test的属性ID
fid = env->GetStaticFieldID(clazz, "iVar", "I");
if (fid == NULL) {
return 0;
}
// 3.获取静态变量test的值
test = env->GetStaticIntField(
clazz,
fid);
LOGI("test","test = %d\n", test);
// 4.修改静态变量num的值
jint ret = 55;
env->SetStaticIntField(clazz, fid, ret);
test = env->GetStaticIntField(
clazz,
fid);
// 删除属部引用
env->DeleteLocalRef(clazz);
return test;
}
extern "C" JNIEXPORT int Java_com_example_jni_1test_MainActivity_CallbackVarTest(
JNIEnv* env,
jobject /* this */,jobject obj) {
jclass clazz;
jfieldID fid;
jstring j_str;
jstring j_newStr;
const char *c_str = NULL;
// 1.获取AccessField类的Class引用
clazz = env->GetObjectClass(obj);
if (clazz == NULL) {
return 0;
}
// 2. 获取AccessField类实例变量str的属性ID
fid = env->GetFieldID(clazz, "str", "Ljava/lang/String;");
if (clazz == NULL) {
return 0;
}
// 3. 获取实例变量str的值
j_str = static_cast<jstring>(env->GetObjectField(obj, fid)); //JNIEnv*, jobject, jfieldID
if (j_str == NULL) {
return 0;
}
// 4. 将unicode编码的java字符串转换成C风格字符串
c_str = env->GetStringUTFChars(j_str, NULL); //JNIEnv*, jstring, jboolean*
if (c_str == NULL) {
return 0;
}
LOGI("test","In C--->ClassField.str = %s\n", c_str);
env->ReleaseStringUTFChars(j_str, c_str); //JNIEnv*, jstring, const char*
// 5. 修改实例变量str的值
j_newStr = env->NewStringUTF("This is new String");
if (j_newStr == NULL) {
return 0;
}
env->SetObjectField(obj, fid, j_newStr); //JNIEnv*, jobject, jfieldID, jobject
// 6.删除局部引用
env->DeleteLocalRef(clazz); //JNIEnv*, jobject
env->DeleteLocalRef(j_str); //JNIEnv*, jobject
env->DeleteLocalRef(j_newStr); //JNIEnv*, jobject
//env->DeleteLocalRef(env,fid);//JNIEnv*, jobject 返回的非object,不能使用 DeleteLocalRef
//使用NewObject就会返回创建出来的实例的局部引用 可 DeleteLocalRef
return 1;
}
extern "C" JNIEXPORT jint JNICALL Java_com_example_jni_1test_MainActivity_add
(JNIEnv *env, jobject, jint x, jint y){
int x1 = x;
int y1 = y;
int sum = (x1+y1)*200;
__android_log_print(ANDROID_LOG_DEBUG,"test","hello5 world");
LOGD("test","hello sum is %d",sum);
//printf("hello3 world");
// std::cout<<"hello4 world"<<std::endl;
return sum;
}
extern "C" JNIEXPORT jintArray JNICALL Java_com_example_jni_1test_MainActivity_updateArray
(JNIEnv *env, jobject, jintArray src,jint len)
{
jintArray dst;
jint *cPArray = env->GetIntArrayElements(src, NULL);
if (cPArray == NULL) {
return 0; // JVM复制原始数据到缓冲区失败
}
// jint i;
for(int i=0;i<len;i++)
{
cPArray[i]=cPArray[i]+100;
LOGD("test","cPArray %d",cPArray[i]);
}
//给java层返回数组方式1
int cInts[len]; //定义一个数组
for (int i = 0; i < len; i++) {
cInts[i] = cPArray[i];
}
dst = env->NewIntArray(len);
if (dst == NULL) {
return NULL; /* out of memory error thrown */
}
//move from the temp structure to the java structure 将native数组转换为java层数组
env->SetIntArrayRegion(dst, 0, sizeof(cInts) / sizeof(cInts[0]),
cInts);
env->ReleaseIntArrayElements(src, cPArray, 0); // 释放可能复制的缓冲区
return dst;
}
extern "C" JNIEXPORT void JNICALL Java_com_example_jni_1test_MainActivity_passmem2C
(JNIEnv *env, jobject, jintArray src,jint len) {
jintArray dst;
int *cPArray = env->GetIntArrayElements(src, NULL);
if (cPArray == NULL) {
return ; // JVM复制原始数据到缓冲区失败
}
for(int i=0;i<len;i++)
{
*(cPArray+i)=i;
LOGD("test","cPArray %d", *(cPArray+i));
}
env->ReleaseIntArrayElements(src, cPArray, 0); // 释放可能复制的缓冲区
}
extern "C" JNIEXPORT jintArray JNICALL Java_com_example_jni_1test_MainActivity_getmem2C
(JNIEnv *env, jobject){
jintArray dst;
int *p = (int *)malloc(10);
if(!p)
{
LOGD("test","malloc is failed");
}
for(int i=0;i<10;i++)
{
*(p+i)=i;
LOGD("test","navtive cPArray %d", *(p+i));
}
dst = env->NewIntArray(10);
if (dst == NULL) {
return NULL; /* out of memory error thrown */
}
//move from the temp structure to the java structure 将native数组转换为java层数组
env->SetIntArrayRegion(dst, 0, 10,p);
return dst;
}