转自:http://blog.csdn.net/huangximin1990/article/details/50830797
Android Studio下JNI环境搭建、编译、运行等可以参考:
http://blog.csdn.net/huangximin1990/article/details/50441400
上篇文章讲述的JNI示例程序主要涉及两个文件:
NativeKit.java
cn_com_losy_jnitest_jni_NativeKit.cpp
注:由于字体调整关系,下列所示代码多了很多<span></span>标签。需要源码的同学可直接到git下载
https://git.oschina.net/huangximin/JniTest-AS.git
NativeKit.java文件(各方法功能注释写得很清楚,不做赘述)
- /**
- * Created by huangximin on 2015/12/24.
- * 说明:本类中以Java层为基本视角
- * Java层向native层传参称为传入
- * native层向Java层传参称为传出
- */
- public class NativeKit {
- private String name = "Java";
- // 回调方法.native层的doCallback方法会调用本方法
- public void callbackForJni(String fromNative) {
- Log.d("jni", "jni string from native:" + fromNative);
- }
- // 加载so文件
- static {
- System.loadLibrary("JniDemo");
- }
- // 求平方.分别测试int/float/double/long的传入和传出
- public native int square(int num);
- public native float square(float num);
- public native double square(double num);
- public native long square(long num);
- // 欢迎词.测试String的传入和传出
- public native String greetings(String username);
- // 获取二维数组.dimon*dimon的int型数组
- public native int[][] getTwoArray(int dimon);
- // 设置name.测试native层直接修改Java类成员变量
- public native void nativeSetName();
- // 回调.这个方法有点绕.native层的doCallback方法会再调用Java层(本类)的callbackForJni方法
- public native void doCallback();
- // 获取User实例.测试native层创建对象,然后返回给Java层
- public native User nativeGetUser();
- // native层获取Java层对象,并获取成员变量
- public native void printUserInfoAtNative(User user);
- // 修改User对象成员变量.同时测试Java层对象的传入、修改、传出
- public native User changeUserInfo(User user);
- // 获取对象列表.测试native层创建Java层对象列表,并返回
- public native ArrayList<User> nativeGetUserList(int num);
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
cn_com_losy_jnitest_jni_NativeKit.cpp文件的内容比较多,按照NativeKit类中方法声明顺序逐个看。
- <span style="font-size:18px;color:#ff0000;">public native int square(int num);</span>
- <span style="font-size:18px;">JNIEXPORT jint JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_square__I
- (JNIEnv * env, jobject obj, jint num) {
- return num * num;
- }</span>
JNIEXPORT 和JNICALL为JNI关键字,不用管它们
jint 对应int (具体数据类型对应详见本文最后)
- Java_cn_com_losy_jnitest_jni_NativeKit_square__I 指明对应的方法
cn.com.losy.jnitest.jni包中的NativeKit类中的square方法
注:由于该类中square方法存在重载现象,而C语言没有重载,JNI的处理方法是在上述方法名最后面增加了
- __I (后面的I表示参数为一个int
JNIEnv * env JNIEnv 是JNI核心,有众多用处,后面会看到
jobject obj 这个jobject需要两种情况分析。上段代码中square方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是obj就是一个NativeKit实例。如果square是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用NativeKit.square()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是obj就是NativeKit.class。
jint num 即NativeKit中square(int num)方法传下来的参数
---------------------------------------
- public native float square(float num);
- public native double square(double num);
- public native long square(long num);
----------------------------------------
- public native String greetings(String username);
- JNIEXPORT jstring JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_greetings
- (JNIEnv * env, jobject obj, jstring jstr) {
- // 读取来自Java层的string
- const char* str = <span style="color:#33cc00;">env->GetStringUTFChars(jstr, false);</span>
- if(str == NULL) {
- return NULL; /* OutOfMemoryError already thrown */
- }
- // 显示string
- std::printf("%s", str);
- std::printf("\n");
- // 释放资源
- <span style="color:#33cc00;">env->ReleaseStringUTFChars(jstr, str);</span>
- // 返回新string
- const char* tmpstr = "I am from Jni";
- <span style="color:#33cc00;">jstring rtstr = env->NewStringUTF(tmpstr);</span>
- return rtstr;
- }
- const char* str = env->GetStringUTFChars(jstr, false);
- const char* tmpstr = "I am from Jni";
- jstring rtstr = env->NewStringUTF(tmpstr);
- public native int[][] getTwoArray(int dimon);
- JNIEXPORT jobjectArray JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_getTwoArray
- (JNIEnv * env, jobject obj, jint dimion) {
- if (dimion > 10) { // 假设 dimion <= 10 ;
- return NULL;
- }
- // 获得一维数组 的类引用,即jintArray类型
- jclass intArrayClass = <span style="color:#009900;">env->FindClass("[I");</span>
- // 构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
- jobjectArray obejctIntArray = <span style="color:#009900;">env->NewObjectArray(dimion, intArrayClass, NULL);</span>
- // 构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组
- for( int i = 0; i< dimion; i++ ) {
- // 构建jint型一维数组
- jintArray intArray = <span style="color:#33cc00;">env->NewIntArray(dimion);</span>
- // 设置jint型一维数组的值
- jint temp[10]; // 初始化一个容器,假设 dimion <= 10 ;
- for( int j = 0; j < dimion; j++) {
- temp[j] = i + j; //赋值
- }
- // 将temp数组复制到intArray
- <span style="color:#33cc00;">env->SetIntArrayRegion(intArray, 0, dimion, temp);</span>
- // 给object对象数组赋值,即保持对jint一维数组的引用
- <span style="color:#33cc00;">env->SetObjectArrayElement(obejctIntArray, i, intArray);</span>
- // 删除局部引用
- <span style="color:#33cc00;">env->DeleteLocalRef(intArray);</span>
- }
- return obejctIntArray; // 返回该对象数组
- }
- public native void nativeSetName();
- JNIEXPORT void JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeSetName
- (JNIEnv * env, jobject obj) {
- jclass cls = <span style="color:#33cc00;">env->GetObjectClass(obj);</span> //获得Java层该对象实例的类引用,即NativeKit类引用
- jfieldID nameFieldId = <span style="color:#33cc00;">env->GetFieldID(cls, "name", "Ljava/lang/String;");</span> //获得属性句柄
- if(nameFieldId == NULL) {
- std::printf(" 没有得到name 的句柄Id \n;");
- }
- jstring javaNameStr = <span style="color:#33cc00;">(jstring) env->GetObjectField(obj ,nameFieldId);</span> // 获得该属性的值
- const char * c_javaName = <span style="color:#33cc00;">env->GetStringUTFChars(javaNameStr , NULL);</span> //转换为 char *类型
- std::printf("%s", c_javaName); //输出显示
- std::printf("\n");
- <span style="color:#33cc00;">env->ReleaseStringUTFChars(javaNameStr , c_javaName);</span> //释放局部引用
- // 构造一个jString对象
- const char * c_ptr_name = "setByNative";
- jstring cName = <span style="color:#33cc00;">env->NewStringUTF(c_ptr_name);</span>
- <span style="color:#33cc00;">env->SetObjectField(obj, nameFieldId, cName);</span> // 设置该字段的值
- }
- public native void doCallback();
- JNIEXPORT void JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_doCallback
- (JNIEnv * env, jobject obj) {
- jclass cls = <span style="color:#33cc00;">env->GetObjectClass(obj);</span> // 获得NativeKit类引用
- jmethodID callbackMethodID = <span style="color:#33cc00;">env->GetMethodID(cls, "callbackForJni", "(Ljava/lang/String;)V");</span> // 获得该回调方法句柄
- if(callbackMethodID == NULL) {
- std::printf("doCallback getMethodId is failed \n");
- }
- jstring native_desc = <span style="color:#33cc00;">env->NewStringUTF("callback From Native");</span>
- // 回调该方法,并且传递参数值
- <span style="color:#33cc00;">env->CallVoidMethod(obj, callbackMethodID, native_desc);</span>
- }
- public native User nativeGetUser();
- JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeGetUser
- (JNIEnv * env, jobject obj) {
- jclass usercls = <span style="color:#33cc00;">env->FindClass("cn/com/losy/jnitest/jni/User");</span> // 获取User对象引用
- //获得得该类型的构造函数 函数名为 <init> 返回类型必须为 void 即 V
- jmethodID constructMID = <span style="color:#33cc00;">env->GetMethodID(usercls,"<init>","(ILjava/lang/String;)V");</span>
- jstring name = <span style="color:#33cc00;">env->NewStringUTF("HXM from Native");</span>
- // 构造一个对象,调用该类的构造函数,并且传递参数
- jobject userojb = <span style="color:#33cc00;">env->NewObject(usercls, constructMID, 11, name);</span>
- return userojb;
- }
- public native void printUserInfoAtNative(User user);
相关的点包含在下一个方法
--------------------------------------
- public native User changeUserInfo(User user);
- JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_changeUserInfo
- (JNIEnv * env, jobject obj, jobject userobj) {
- jclass usercls = <span style="color:#33cc00;">env->GetObjectClass(userobj);</span> // 或得User类引用
- if(usercls == NULL) {
- std::printf("GetObjectClass failed \n");
- }
- jfieldID ageFieldID = <span style="color:#33cc00;">env->GetFieldID(usercls,"age","I");</span> // 获得得User类的属性id
- jfieldID nameFieldID = <span style="color:#33cc00;">env->GetFieldID(usercls,"name","Ljava/lang/String;");</span> // 获得属性ID
- jint age = <span style="color:#33cc00;">env->GetIntField(userobj, ageFieldID);</span> // 获得属性值
- jstring name = <span style="color:#33cc00;">(jstring)env->GetObjectField(userobj, nameFieldID);</span>// 获得属性值
- const char * c_name = <span style="color:#33cc00;">env->GetStringUTFChars(name, false);</span>// 转换成 char *
- // 显示user信息
- std::printf("show user info age:%d name:%s", age, c_name);
- <span style="color:#33cc00;"> env->ReleaseStringUTFChars(name, c_name); </span>//释放引用
- // 构造一个jString对象
- const char * c_ptr_name = "quyuan";
- jstring cName = <span style="color:#33cc00;">env->NewStringUTF(c_ptr_name);</span>
- <span style="color:#33cc00;">env->SetObjectField(userobj, nameFieldID, cName);</span> // 设置该字段的值
- <span style="color:#33cc00;">env->SetIntField(userobj, ageFieldID, 55);</span> // 设置该字段的值
- return userobj;
- }
- public native ArrayList<User> nativeGetUserList(int num);
- JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeGetUserList
- (JNIEnv * env, jobject obj, jint size) {
- jclass listcls = <span style="color:#33cc00;">env->FindClass("java/util/ArrayList");</span>// 获得ArrayList类引用
- if(listcls == NULL) {
- std::printf("listcls is null \n");
- }
- jmethodID list_construct = <span style="color:#33cc00;">env->GetMethodID(listcls , "<init>","()V");</span>// 获得得构造函数Id
- jobject listobj = <span style="color:#33cc00;">env->NewObject(listcls, list_construct);</span> // 创建一个Arraylist集合对象
- // 获得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;
- jmethodID list_add = <span style="color:#33cc00;">env->GetMethodID(listcls,"add","(Ljava/lang/Object;)Z");</span>
- jclass usercls = <span style="color:#33cc00;">env->FindClass("cn/com/losy/jnitest/jni/User"); </span>// 获得User类引用
- //获得该类型的构造函数 函数名为 <init> 返回类型必须为 void 即 V
- jmethodID user_costruct = <span style="color:#33cc00;">env->GetMethodID(usercls , "<init>", "(ILjava/lang/String;)V");</span>
- for(int i = 0; i < size; i++) {
- jstring str = <span style="color:#33cc00;">env->NewStringUTF("Native");</span>
- //通过调用该对象的构造函数来new 一个 Student实例
- jobject stu_obj = <span style="color:#33cc00;">env->NewObject(usercls , user_costruct, 10, str);</span> //构造一个对象
- <span style="color:#33cc00;">env->CallBooleanMethod(listobj, list_add, stu_obj);</span> // 执行Arraylist类实例的add方法,添加一个stu对象
- }
- return listobj ;
- }
Java 类型 | 本地 C 类型 |
实际表示的 C 类型
(Win32)
| 说明 |
---|---|---|---|
boolean | jboolean | unsigned char | 无符号,8 位 |
byte | jbyte | signed char | 有符号,8 位 |
char | jchar | unsigned short | 无符号,16 位 |
short | jshort | short | 有符号,16 位 |
int | jint | long | 有符号,32 位 |
long | jlong | __int64 | 有符号,64 位 |
float | jfloat | float | 32 位 |
double | jdouble | double | 64 位 |
void | void | N/A | N/A |