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;
}
其中,值得注意的是读取String需
const char* str = env->GetStringUTFChars(jstr, false);
构造String需
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 |