天天看點

Android NDK學習:native 通過JNI調用java的屬性跟方法

native調用Java的普通屬性

  • Java 代碼
public class JniUtils {
    static{
        System.loadLibrary("JNIEnvOne");
    }
    public String name = "liu";
    //通路普通屬性
    public native void modifField();
}
           
  • C 代碼
//調用普通屬性
JNIEXPORT void JNICALL Java_com_liu_JniUtils_modifField
(JNIEnv * env, jobject obj){
    //擷取類
    jclass jclazz = (*env)->GetObjectClass(env, obj);
    //擷取屬性,屬性簽名
    jfieldID field = (*env)->GetFieldID(env, jclazz, "name", "Ljava/lang/String;");
    //拿到屬性的值(JNI的)
    //get<Type>Field 模式,不同的類型
    jstring jojb = (*env)->GetObjectField(env, obj, field);
    //轉成c的字元串
    //isCopy 是否指派的意思。
    jboolean isCopy = NULL;
    char* c_str = (*env)->GetStringChars(env, jojb, &isCopy);
    if (isCopy){
        printf("%s\n", "this str is copy");
    }
    char* mid = "I am from C";
    //拼接C的字元串
    strcat(c_str, mid);

    //set<Type>Field 模式
    (*env)->SetObjectField(env, obj, field, (*env)->NewStringUTF(env, c_str));
    //釋放
    (*env)->ReleaseStringChars(env, jojb, c_str);

}
           

調用步驟:

- 擷取類。 (*env)->GetObjectClass(env, obj);

- 擷取屬性簽名。(*env)->GetFieldID(env, jclazz, “name”, “Ljava/lang/String;”);

- 拿到屬性的值。(*env)->GetObjectField(env, obj, field);

- 把值轉換成C可操作的類型。(*env)->GetStringChars(env, jojb, &isCopy);

- 操作屬性的值。strcat(c_str, mid)/重新指派等等

- 轉換成JNI需要的類型。((*env)->NewStringUTF(env, c_str))

- 設定到屬性裡面。SetObjectField

- 把建立的字元串釋放。(*env)->ReleaseStringChars(env, jojb, c_str);

native調用Java的靜态屬性

  • Java 代碼
public class JniUtils {
    static{
        System.loadLibrary("JNIEnvOne");
    }
    public static String staticName = "liu";
    //通路靜态屬性
    public native void modifStaticField();
}
           
  • C 代碼
//擷取靜态屬性
JNIEXPORT void JNICALL Java_com_liu_JniUtils_modifStaticField
(JNIEnv * env, jobject obj){
    //先擷取jclass
    jclass cls = (*env)->GetObjectClass(env, obj);
    //擷取屬性簽名
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "staticName", "Ljava/lang/String;");
    //擷取屬性
    jstring field = (*env)->GetStaticObjectField(env, cls, fid);

    //轉換成c的類型
    char* str = (*env)->GetStringChars(env, field, NULL);

    char* s = "static str";

    strcat(s, str);

    jstring result = (*env)->NewStringUTF(env, s);
    (*env)->SetStaticObjectField(env, cls, fid, result);

    (*env)->ReleaseStringChars(env, field, str);

}
           

步驟:

- 擷取類

- 擷取屬性簽名

- 擷取屬性

- 轉換成C可操作的類型

- 操作值

- 轉換成JNI需要的類型

- 設定屬性

- 釋放資源

native調用Java的普通方法

  • Java代碼
public class JniUtils {
    static{
        System.loadLibrary("JNIEnvOne");
    }
    //通路普通方法
    public native void callMethod(int count);
    public void normalMethod(int count){
        System.out.println("普通方法被調用了。count:"+count);
    }
}
           
  • C 代碼
//調用普通方法
JNIEXPORT void JNICALL Java_com_liu_JniUtils_callMethod
(JNIEnv * env, jobject obj,jint count){
    //擷取類
    jclass cls = (*env)->GetObjectClass(env, obj);
    //擷取方法簽名
    jmethodID mid = (*env)->GetMethodID(env, cls, "normalMethod", "(I)V");
    //調用方法
    (*env)->CallObjectMethod(env, obj, mid, count);
}
           

步驟:

- 擷取類

- 擷取方法簽名

- 調用方法

native調用Java的靜态方法

  • Java 代碼
public class JniUtils {
    static{
        System.loadLibrary("JNIEnvOne");
    }
    //通路靜态方法
    public native void callStaticMethod();
    public static void staticMethod(){
        System.out.println("static 方法被調用了。count:");
    }
}
           
  • C 代碼
//調用靜态方法
JNIEXPORT void JNICALL Java_com_liu_JniUtils_callStaticMethod
(JNIEnv * env, jobject obj, jint count){
    //擷取類
    jclass cls = (*env)->GetObjectClass(env, obj);
    //擷取方法簽名
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "staticMethod", "()V");
    //調用方法
    (*env)->CallStaticVoidMethod(env, cls, mid);
}
           

Java層調用native,傳回亂碼問題及調用構造方法

  • Java 代碼
public class JniUtils {
    static{
        System.loadLibrary("JNIEnvOne");
    }
    //通路構造方法
    public native void callConstructMethod();
    public String name = "liu";
}
           
  • C 代碼
//解決字元串的亂碼問題,及通路類的構造器
//通過new String(byte[],charset)。轉換編碼來解決亂碼問題
jstring conv_str_to_java(JNIEnv* env, char* str){
    //先找到類
    jclass cls = (*env)->FindClass(env, "java/lang/String");
    //擷取構造器的方法簽名
    jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "([BLjava/lang/String;)V");
    //需要轉換的字元串長度
    int arr_len = strlen(str);
    //jbyte-char。初始化一個同等長度的字元數組
    jbyteArray arr = (*env)->NewByteArray(env, arr_len);
    //str數組指派到jbytArray
    (*env)->SetByteArrayRegion(env, arr, , arr_len, str);

    //把需要的編碼,轉換成JNI需要的類型
    jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
    //調用String的構造方法,初始化String類
    jstring result = (*env)->NewObject(env, cls, mid, arr, charsetName);

    return result;
}

//調用普通屬性
JNIEXPORT void JNICALL Java_com_liu_JniUtils_callConstructMethod
(JNIEnv * env, jobject obj){
    //擷取類
    jclass jclazz = (*env)->GetObjectClass(env, obj);
    //擷取屬性,屬性簽名
    jfieldID field = (*env)->GetFieldID(env, jclazz, "name", "Ljava/lang/String;");
    //拿到屬性的值(JNI的)
    //get<Type>Field 模式,不同的類型
    jstring jojb = (*env)->GetObjectField(env, obj, field);
    //轉成c的字元串
    char* c_str = (*env)->GetStringChars(env, jojb,NULL);
    char* mid = "我是C的字元串";
    //拼接C的字元串
    strcat(c_str, mid);

    //set<Type>Field 模式
    //這樣是有亂碼的
    //(*env)->SetObjectField(env, obj, field, (*env)->NewStringUTF(env, c_str));

    //通過上面的函數轉換後,就沒有亂碼了
    (*env)->SetObjectField(env, obj, field, conv_str_to_java(env,c_str));
    //釋放
    (*env)->ReleaseStringChars(env, jojb, c_str);


}
           

native調用Java的普通的類的方法

  • Java 代碼
public class JniUtils {
    static{
        System.loadLibrary("JNIEnvOne");
    }
    //通路普通類的普通方法
    public native long callNormalClassMethod();
}
           
  • C 代碼
//通路普通類的方法
JNIEXPORT jlong JNICALL Java_com_liu_JniUtils_callNormalClassMethod
(JNIEnv * env, jobject obj){
    //找到類
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //擷取構造方法的簽名
    jmethodID construct_id = (*env)->GetMethodID(env, cls, "<init>", "()V");
    //初始化 類
    jobject date_obj = (*env)->NewObject(env, cls,construct_id);
    //擷取方法的簽名
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");

    //通路方法
    jlong t = (*env)->CallLongMethod(env, date_obj, mid);
    return t;
}
           

檢視類屬性和方法的簽名

  • cd native方法生成的類的檔案夾下
  • 執行javap -s -p xx.xx.JniUtils