一、修改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