一、修改java类中非静态属性:
- java类中代码:
public String str = "hello";
/* JNI改变类中变量str的值 */
public native void changeStr();
- 中间头文件省略,直接C++代码:
JNIEXPORT void JNICALL Java_com_example_jnitest_JNITest_changeStr
(JNIEnv *env, jobject jobj)
{
/* 获取JNITest的字节码 */
jclass clazz = env->GetObjectClass(jobj);
/* 获取java类中变量num的Field */
jfieldID jId = env->GetFieldID(clazz, "str", "Ljava/lang/String;");
/* 获取java中str的值 */
jstring jstr = (jstring)env->GetObjectField(jobj, jId);
/* 将jstring转化为char */
char* cstr = (char* )env->GetStringUTFChars(jstr, NULL);
/* 修改值 */
char newstr[20] = "world!";
strcat(newstr, cstr);
/* 将char转成jstring */
jstring jstrnew = env->NewStringUTF(newstr);
/* 重新设置到java类变量里面去 */
env->SetObjectField(jobj, jId, jstrnew);
/* 释放JNI层资源 */
env->ReleaseStringUTFChars(jstr, cstr);
}
- 测试代码:
JNITest obj = new JNITest();
obj.changeStr();
Toast.makeText(MainActivity.this, obj.str, Toast.LENGTH_SHORT).show();
二、 修改java类中静态属性:
5. java代码:
public static int age = 18;
/* JNI改变类中static变量age的值 */
public native void changeAge();
- C++代码实现:
JNIEXPORT void JNICALL Java_com_example_jnitest_JNITest_changeAge
(JNIEnv *env, jobject jobj)
{
/* 获取JNITest的字节码 */
jclass clazz = env->GetObjectClass(jobj);
/* 获取java类中变量num的Field */
jfieldID jId = env->GetStaticFieldID(clazz, "age", "I");
/* 获取num的值 */
jint jAge = env->GetStaticIntField(clazz, jId);
jAge = 28;
env->SetStaticIntField(clazz, jId, jAge);
}
- java测试代码:
JNITest obj = new JNITest();
obj.changeAge();
Toast.makeText(MainActivity.this, "age = " + JNITest.age, Toast.LENGTH_SHORT).show();
三、总结:
5. JNI层就是JAVA层和C/C++层中间的桥梁,java类型数据想要在C/C++层进行修改,必须先要转换为jni类型,然后借助传下来的java虚拟机变量env调用合适的函数指针将其转换为C/C++中可以访问的变量类型,进行操作,完成之后再通过JNI转换回去;
6. 静态和非静态变量的区别是调用的JNI方法也会变为静态或者非静态;
四、补充:
- 如何查看类中成员变量和方法的签名:
a. 如果是java公共类,直接使用cmd的javap指令即可,示例:
javap -s java.lang.String
b.如果是自己编写的类,Android studio 3.5版本:
将Android studio的视图选为project,进入app->build->javac->debug->classes,右键选择show in explorer,然后进入classes目录,弹出cmd,输入上述指令,示例:
javap -s com.example.jnitest.JNITest
当然,也可以直接使用Android studio的terminal进行操作,这里顺便补充一下如何获取自己编写的类的全类名,鼠标类名右键:
选择copy reference即可;
- 如何在C/C++代码中添加打印:
a. 在Android.mk中加入如下代码:
LOCAL_PATH := $(call my-dir) #接收当前Android.mk文件的绝对路径
include $(CLEAR_VARS) #类似于工具初始化的操作
LOCAL_MODULE := libhellojni #模块名
LOCAL_SRC_FILES := hello.cpp #依赖的源文件
LOCAL_LDLIBS := -llog #引用log对应的库文件
include $(BUILD_SHARED_LIBRARY) #将该模块编译成一个动态库
LOCAL_LDLIBS := -llog为引入log需要的库文件,库的话会从system/lib下面去动态加载;
b. 代码中的修改:
#include <Android/log.h>
#define LOG "JNITest"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG,__VA_ARGS__)
进入上述代码之后我们可以在文件中直接使用LOGE进行打印,并添加更多的打印级别。
代码路径:
https://github.com/jiyi666/apk-demo/tree/master/JNITest