Native access Java fields
- KotlinFields.kt
package com.practice
var topLevelField = "top"
class KotlinFields {
var number: Int = 3
companion object {
@JvmField
var companionField: Double = 9.0
}
}
Check the signature after compile it.
$ ls
KotlinFields$Companion.class KotlinFields.class KotlinFieldsKt.class Lib.class MainActivity.class
$ javap -p -s com/practice/KotlinFieldsKt.class
Compiled from "KotlinFields.kt"
public final class com.practice.KotlinFieldsKt {
private static java.lang.String topLevelField;
descriptor: Ljava/lang/String;
public static final java.lang.String getTopLevelField();
descriptor: ()Ljava/lang/String;
public static final void setTopLevelField(java.lang.String);
descriptor: (Ljava/lang/String;)V
static {};
descriptor: ()V
}
$ javap -p -s com/practice/KotlinFields\$Companion.class
Compiled from "KotlinFields.kt"
public final class com.practice.KotlinFields$Companion {
private com.practice.KotlinFields$Companion();
descriptor: ()V
public com.practice.KotlinFields$Companion(kotlin.jvm.internal.DefaultConstructorMarker);
descriptor: (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
}
$ javap -p -s com/practice/KotlinFields.class
Compiled from "KotlinFields.kt"
public final class com.practice.KotlinFields {
private int number;
descriptor: I
public static double companionField;
descriptor: D
public static final com.practice.KotlinFields$Companion Companion;
descriptor: Lcom/practice/KotlinFields$Companion;
public final int getNumber();
descriptor: ()I
public final void setNumber(int);
descriptor: (I)V
public com.practice.KotlinFields();
descriptor: ()V
static {};
descriptor: ()V
}
- MainActivity.kt
package com.practice
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
topLevelField = "top-java"
KotlinFields.companionField = 3.0
val fields = KotlinFields()
fields.number = 5
Lib().callNative(fields)
println("top level field is $topLevelField")
println("companion field is ${KotlinFields.companionField}")
println("member is ${fields.number}")
}
}
- native-lib.cpp
#include <jni.h>
#include <android/log.h>
constexpr const char *TAG = "nativeCode";
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
constexpr const char *TOP_LEVEL_CLASS_NAME = "com/practice/KotlinFieldsKt";
constexpr const char *TOP_LEVEL_FIELD_NAME = "topLevelField";
constexpr const char *CLASS_NAME = "com/practice/KotlinFields";
constexpr const char *MEMBER_NAME = "number";
constexpr const char *COMPANION_FIELD_NAME = "companionField";
constexpr const char *TYPE_STR = "Ljava/lang/String;";
constexpr const char *TYPE_INT = "I";
jclass getClass(JNIEnv *env, const char *path) {
jclass cls = env->FindClass(path);
if (cls == nullptr) {
LOGE("cls %s not found.", path);
}
return cls;
}
jfieldID getStaticFieldId(JNIEnv *env, jclass cls, const char *methodName, const char *sig) {
jfieldID result = env->GetStaticFieldID(cls, methodName, sig);
if (result == nullptr) {
LOGE("static field id for %s(%s) not found.", methodName, sig);
}
return result;
}
jstring getTopLevelField(JNIEnv *env) {
jclass cls = getClass(env, TOP_LEVEL_CLASS_NAME);
jfieldID fieldId = getStaticFieldId(env, cls, TOP_LEVEL_FIELD_NAME, TYPE_STR);
auto result = (jstring) env->GetStaticObjectField(cls, fieldId);
env->DeleteLocalRef(cls);
return result;
}
void setTopLevelField(JNIEnv *env, const char *text) {
jclass cls = getClass(env, TOP_LEVEL_CLASS_NAME);
jfieldID fieldId = getStaticFieldId(env, cls, TOP_LEVEL_FIELD_NAME, TYPE_STR);
jstring javaText = env->NewStringUTF(text);
env->SetStaticObjectField(cls, fieldId, javaText);
env->DeleteLocalRef(javaText);
env->DeleteLocalRef(cls);
}
jdouble getCompanionField(JNIEnv *env) {
jclass cls = getClass(env, CLASS_NAME);
jfieldID fieldId = getStaticFieldId(env, cls, COMPANION_FIELD_NAME, "D");
auto result = env->GetStaticDoubleField(cls, fieldId);
env->DeleteLocalRef(cls);
return result;
}
void setCompanionField(JNIEnv *env, jdouble value) {
jclass cls = getClass(env, CLASS_NAME);
jfieldID fieldId = getStaticFieldId(env, cls, COMPANION_FIELD_NAME, "D");
env->SetStaticDoubleField(cls, fieldId, value);
env->DeleteLocalRef(cls);
}
jint getMember(JNIEnv *env, jobject obj) {
jclass cls = env->GetObjectClass(obj);
jfieldID fieldId = env->GetFieldID(cls, MEMBER_NAME, TYPE_INT);
jint result = env->GetIntField(obj, fieldId);
env->DeleteLocalRef(cls);
return result;
}
void setMember(JNIEnv *env, jobject obj, jint value) {
jclass cls = env->GetObjectClass(obj);
jfieldID fieldId = env->GetFieldID(cls, MEMBER_NAME, TYPE_INT);
env->SetIntField(obj, fieldId, value);
env->DeleteLocalRef(cls);
}
extern "C" JNIEXPORT void JNICALL
Java_com_practice_Lib_callNative(JNIEnv *env, jobject /* this */, jobject obj) {
// get top level field
jstring topStaticField = getTopLevelField(env);
const char *topStaticFieldNative = env->GetStringUTFChars(topStaticField, JNI_FALSE);
LOGD("top static field is %s", topStaticFieldNative);
env->ReleaseStringUTFChars(topStaticField, topStaticFieldNative);
env->DeleteLocalRef(topStaticField);
// set top level field
setTopLevelField(env, "native top level value");
// get companion field
jdouble companionField = getCompanionField(env);
LOGD("companion field is %lf", companionField);
// set companion field
setCompanionField(env, 98.7);
// get member
LOGD("member is %d", getMember(env, obj));
// set member
setMember(env, obj, 789);
}
- LogCat
D/nativeCode: top static field is top-java
D/nativeCode: companion field is 3.000000
D/nativeCode: member is 5
I/System.out: top level field is native top level value
I/System.out: companion field is 98.7
I/System.out: member is 789