使用NDK進行JNI編程與實踐三:在C/C++代碼中修改java類中屬性

一、修改java類中非靜態屬性:

  1. java類中代碼:
    public String str = "hello";
    /* JNI改變類中變量str的值 */
    public native void changeStr();
  1. 中間頭文件省略,直接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);
}
  1. 測試代碼:
	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();
  1. 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);
}
  1. 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方法也會變爲靜態或者非靜態;

四、補充:

  1. 如何查看類中成員變量和方法的簽名:
    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即可;

  1. 如何在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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章