Android中关于JNI 的学习(四)简单的例子,温故而知新
来源:互联网 发布:手游数据分析 编辑:程序博客网 时间:2024/06/05 18:58
在第零篇文章简单地介绍了JNI编程的模式之后,后面两三篇文章,我们又针对JNI中的一些概念做了一些简单的介绍,也不知道我到底说的清楚没有,但相信很多童鞋跟我一样,在刚开始学习一个东西的时候,入门最好的方式就是一个现成的例子来参考,慢慢研究,再学习概念,再回过来研究代码,加深印象,从而开始慢慢掌握。
今天我们就再来做一个小Demo,这个例子会比前面稍微复杂一点,但是如果阅读过前面几篇文章的话,理解起来也还是很简单的。很多东西就是这样,未知的时候很可怕,理解了就很简单了。
1)我们首先定义一个Java类,里面包含几个native方法,如下:
public class ParamTransferTest {public static int testval = 1;public native void changeTestVal();public native int add(int x, int y);public native String addTail(String tail);public native int[] changeArray(int[] arr);}
2)利用javah工具生成对应的头文件,如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_lms_jni_ParamTransferTest */#ifndef _Included_com_lms_jni_ParamTransferTest#define _Included_com_lms_jni_ParamTransferTest#ifdef __cplusplusextern "C" {#endif/* * Class: com_lms_jni_ParamTransferTest * Method: changeTestVal * Signature: ()V */JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal (JNIEnv *, jobject);/* * Class: com_lms_jni_ParamTransferTest * Method: add * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_lms_jni_ParamTransferTest_add (JNIEnv *, jobject, jint, jint);/* * Class: com_lms_jni_ParamTransferTest * Method: addTail * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_lms_jni_ParamTransferTest_addTail (JNIEnv *, jobject, jstring);/* * Class: com_lms_jni_ParamTransferTest * Method: changeArray * Signature: ([I)[I */JNIEXPORT jintArray JNICALL Java_com_lms_jni_ParamTransferTest_changeArray (JNIEnv *, jobject, jintArray);#ifdef __cplusplus}#endif#endif
上面就生成了对应的方法,在上面我们可以看到前面文章所介绍过的方法名称以Java开头,方法签名等信息,对吧。
3)编写 C 文件,如下:
#include <stdio.h>#include <stdlib.h>#include "com_lms_jni_ParamTransferTest.h"#include <android/log.h>#include <jni.h>#include <malloc.h>#define LOG_TAG "System.out"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)char* Jstring2CStr(JNIEnv * env, jstring str){char * rtn = NULL;jclass clsstring = (*env)->FindClass(env, "java/lang/String");//通过FindClass方法获得Java的String类jstring strencode = (*env)->NewStringUTF(env, "UTF-8");//调用NewStringUTF方法,获得"UTF-8"字符串,作为编码jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");//获取String类的getBytes方法jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, str, mid, strencode);//调用String类的getBytes方法jsize alen = (*env)->GetArrayLength(env, barr);//获得数组长度jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);//获得数组的首地址,C/C++中数组的首元素就是一个指针if(alen > 0){rtn = (char*) malloc(alen + 1);memcpy(rtn, ba, alen);rtn[alen] = 0;}//上面这一步是将数组的值复制到一个char*的数组中,也就是C/C++的char数组,因为C/C++没有字符串概念,最后以0结尾。(*env)->ReleaseByteArrayElements(env, barr, ba, 0);//释放内存return rtn;}/* * Class: com_lms_jni_ParamTransferTest * Method: changeTestVal * Signature: ()V */JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal (JNIEnv * env, jobject obj){jclass clazz = (*env)->GetObjectClass(env,obj);//获得obj对应的类,也就是ParamTransferTestjint val = (*env)->GetStaticIntField(env, clazz,(*env)->GetStaticFieldID(env, clazz,"testval","I"));//获取字段testval的值LOGI("before change testval = %d", val);//添加Log信息val = val + 1;LOGI("after change testval = %d", val);(*env)->SetStaticIntField(env, clazz,(*env)->GetStaticFieldID(env, clazz,"testval","I"),val);//设置字段testval的值}/* * Class: com_lms_jni_ParamTransferTest * Method: add * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_lms_jni_ParamTransferTest_add (JNIEnv * env, jobject obj, jint x, jint y){LOGI("x = %d", x);LOGI("y = %d", y);return x + y; //返回参数x和y的和}/* * Class: com_lms_jni_ParamTransferTest * Method: addTail * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_lms_jni_ParamTransferTest_addTail (JNIEnv * env, jobject obj, jstring str){char* p = Jstring2CStr(env,str);//将Java中的string转化为C/C++中的char数组LOGI("str = %s", p);char* newStr = " Tail ";return (*env)->NewStringUTF(env, strcat(p, newStr));//调用strcat函数连接两个char数组,将通过NewStringUTF返回。}/* * Class: com_lms_jni_ParamTransferTest * Method: changeArray * Signature: ([I)[I */JNIEXPORT jintArray JNICALL Java_com_lms_jni_ParamTransferTest_changeArray (JNIEnv * env, jobject obj, jintArray ja){int len = (*env)->GetArrayLength(env, ja);/获取数组长度LOGI("len = %d", len);LOGI("address = %#x", &ja);jint* arr = (*env)->GetIntArrayElements(env, ja, 0);//将数组中的所有元素存入以jint*为首地址的数组中(数组的首地址就是数组名)int i = 0;for(;i < len; i++){LOGI("arr[%d] = %d", i, *(arr + i));*(arr + i) += 10;} //数组中每个元素的值加上10return ja;由于GetIntArrayElements的最后一个参数是0,即表明获取的数组是不复制的,即它们操作同一块内存,所以返回哪个数组都是ok的return ja;}
对应这四个方法,在上面都添加了一些注释,大家看着注释,自己研究一下逻辑,也就清楚了这几个方法实现的功能是什么,很简单的。
4)编写Android.mk文件,如下:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := com_lms_jni_HwDemoLOCAL_SRC_FILES := \HwDemo.c \JniTest.c \ParamTransferTest.cLOCAL_LDLIBS += -lloginclude $(BUILD_SHARED_LIBRARY)
在这里有一点的注意的是,编译多个文件的时候,要利用反斜杠 "\"来进行换行,区分不同的C/C++文件。
而这里LOCAL_LDLIBS是JNI中运用log所需要添加的动态包,下一篇文章会讲。
5)编写好Android.mk文件之后,就利用ndk-build文件进行编译,文件结构如下:
在jni目录下运行ndk-build,如下:
到这里,关于JNI层的实现就结事了,下面就是Java端的事情。
6)Activity中调用,如下:
public class HwDemo extends Activity {static {System.loadLibrary("com_lms_jni_HwDemo");//加载动态包,名称就是Android.mk中的Module名称。}public native String printHello();@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);...TextView tvAdd = (TextView)findViewById(R.id.tvAdd);TextView tvString = (TextView)findViewById(R.id.tvString);TextView tvArray = (TextView)findViewById(R.id.tvArray);TextView tvChangeTestVal = (TextView)findViewById(R.id.tvChangeTestVal);ParamTransferTest ptt = new ParamTransferTest();//调用changeTestVa()方法ptt.changeTestVal();tvChangeTestVal.setText("" + ptt.testval);//调用add方法int sum = ptt.add(1, 2);tvAdd.setText(String.valueOf(sum));//调用addTail方法tvString.setText(ptt.addTail("lms"));//调用changeArray方法int[] newArr = ptt.changeArray(new int[]{1,2});StringBuilder sb = new StringBuilder();sb.append("[");sb.append(newArr[0]).append(",").append(newArr[1]);sb.append("]");tvArray.setText(sb.toString());}}
7)结果显示:
关于如果在Java层调用JNI方法,还有在JNI层调用Java的方法,并操作Java的对象,相信经过这一个例子,再结合前面几篇文章所讲的东西,大家应该能够对JNI的作用和应用有一个比较基本的了解了。
Android中底层框架的实现,尤其是在启动的时候,Native层去加载一个系统核心服务,或者启动Zygote虚拟机的时候,用到了大量的JNI层面的框架,大家如果对JNI熟悉了解了,再去了解这些框架的东西,会有很大的帮助的。
结束。
- Android中关于JNI 的学习(四)简单的例子,温故而知新
- Android中关于JNI 的学习(零)简单的例子,简单地入门
- Android中关于JNI 的学习(零)简单的例子,简单地入门
- Android中关于JNI 的学习(六)JNI中注册方法的实现
- JNI简单的例子
- Android中关于JNI 的学习(三)在JNI层访问Java端对象
- Android中关于JNI 的学习(三)在JNI层访问Java端对象
- java中一些简单的基础知识,温故而知新
- Android中关于ExpandableListView用法的一个简单小例子
- Android中关于JNI 的学习(二)对于JNI方法名,数据类型和方法签名的一些认识
- Android中关于JNI 的学习(二)对于JNI方法名,数据类型和方法签名的一些认识
- Android中关于JNI 的学习(二)对于JNI方法名,数据类型和方法签名的一些认识
- Android中关于JNI 的学习(五)在C文件中使用LogCat
- jni学习简单例子
- Android Studio NDK环境配置与JNI简单的例子
- Android Studio 一个简单的非传统Jni例子
- Android中关于JNI 的学习(一)对于JNIEnv的一些认识
- Android中关于JNI 的学习(一)对于JNIEnv的一些认识
- dxdfdfdfhdf
- Ubuntu 12.04.04 LTS server版本FTP server功能配置
- 【ExtJS 4.x学习教程】(0)简介
- win 8 虚拟wifi bat
- 奔五的人,准备学习iOS开发
- Android中关于JNI 的学习(四)简单的例子,温故而知新
- 人生学会随缘,才能活得自在。随不是跟随,是顺其自然
- INSTALL PKG-CONFIG FOR MAC OSX
- OpenGL基础图形编程
- HTTP响应代码
- 设计模式 - 策略模式(Strategy Pattern) 详解
- Linux挂在U盘的方法
- 使用jni直接访问java对象中的字段
- 欧拉角中的万向节死锁