i have a textview in xml file with "tv" id
if i declare
TextView tv ;
tv= findViewByID(R.id.tv);
then i can call setText Method with this part
extern "C"
JNIEXPORT void JNICALL
Java_org_lotka_brilliance_activity_MainActivity_textViewInit(JNIEnv *env, jobject
thiz) {
jclass MainCLass = env->FindClass("org/lotka/brilliance/activity/MainActivity");
jclass TextViewClass = env->FindClass("android/widget/TextView");
jfieldID tvfieild = env->GetFieldID(MainCLass , "tv",
"Landroid/widget/TextView;");
jmethodID tvsettext = env->GetMethodID(TextViewClass , "setText", "
(Ljava/lang/CharSequence;)V");
jobject jobject1 = env->GetObjectField(thiz , tvfieild);
env->CallVoidMethod(jobject1 ,tvsettext , env->NewStringUTF("hello from jni"));
}
im using the TextView which is declared in java part , now the question is that , if i want to find TextView id in jni what should i do ??
There will be a generated class com.your.package.R.id which contains a bunch of static ints with the view IDs. So it's just a matter of reading the particular field you're interested in, and then calling findViewById to get the TextView.
I'm not sure why anyone would want to manipulate their app's views in this cumbersome way, but it can be done:
jclass idClass = env->FindClass("com/your/package/R$id");
jfieldID tvField = env->GetStaticFieldID(idClass, "tv", "I");
jint tvId = env->GetStaticIntField(idClass, tvField);
// thiz is a jobject corresponding to the calling Activity instance
jmethodID findViewById = env->GetMethodID(env->GetObjectClass(thiz), "findViewById", "(I)Landroid/view/View;");
jobject tv = env->CallObjectMethod(thiz, findViewById, tvId);
jmethodID setText = env->GetMethodID(env->GetObjectClass(tv), "setText", "(Ljava/lang/CharSequence;)V");
env->CallVoidMethod(tv, setText, env->NewStringUTF("Hello from JNI!"));
Related
After update my project to support Android 10, a crash produced in the JNI level related to non-SDK interface restrictions in Android 10 :
JNI DETECTED ERROR IN APPLICATION: JNI SetIntField called with pending exception java.lang.NoSuchFieldError: no "I" field "value" in class "Ljava/lang/Integer;" or its superclasses
which come from this part of code :
jclass clazz = (*env)->GetObjectClass(env, outputObj);
jfieldID mi = (*env)->GetFieldID(env, clazz, "value", "I");
(*env)->SetIntField(env, outputObj, mi, pListLen);
To fix that i replace it by :
jclass clazz = (*env)->GetObjectClass(env, outputObj);
jmethodID intValueMethod = (*env)->GetMethodID(env, clazz, "intValue", "()I");
jint result = (*env)->CallIntMethod(env, outputObj,intValueMethod,pListLen);
After that my application doesn't crash and find the integer value correctly, but i want to set the result integer on Java code using the same method SetIntField.
Could you please give me a way or method to set the result on the Java part.
As changing the intger field is not a good practice on JNI part, so i changed the way instead changing it by reflection method
jclass clazz = (*env)->GetObjectClass(env, outputBufLen);
jmethodID value_of = (*env)->GetStaticMethodID(env,clazz, "valueOf", "(I)Ljava/lang/Integer;");
jobject result = (*env)->CallStaticObjectMethod(env,clazz, value_of, pOutBufLen);
I set the result under an int Array and i return it :
int result = (*env)->CallStaticObjectMethod(env,clazz, value_of, pOutBufLen);
jintArray resultArray = (*env)->NewIntArray(env, 2);
jint fill[2];
fill[0] = result;
fill[1] = (jint) pOutBufLen;
(*env)->SetIntArrayRegion(env, result, 0, 2, fill);
return (resultArray);
I have ArrayList<Pair<Pair<Float, Float>, Pair<Float, Float>>> on Java side and want to use this data in JNI.
What methods and classes to use to convert to std::pair<std::pair<float, float>, std::pair<float, float>>
I tried following
jclass pairClass = env->FindClass("android/util/Pair");
jfieldID pairGetKey = env->GetFieldID(pairClass, "first", "java/util/Objects");
pairGetKey is always null
The type of the field has to be given as a signature; that is, you need to use the int -> I, T[] -> [T, reference.Type -> Lreference/Type; encoding. Also, the type of the field is java.lang.Object, not java.util.Objects.
jfieldID first = env->GetFieldID(pairClass, "first", "Ljava/lang/Object;");
jfieldID second = env->GetFieldID(pairClass, "second", "Ljava/lang/Object;");
The rest is tedious, but not hard:
jfloat extract_float(JNIEnv *env, jobject f) {
// Note the syntax of signatures: float floatValue() has signature "()F"
return env->CallFloatMethod(f,
env->GetMethodID(env->FindClass("java/lang/Float"), "floatValue", "()F"));
}
std::pair<jobject, jobject> extract_pair(JNIEnv *env, jobject p) {
jclass pairClass = env->FindClass("android/util/Pair");
jfieldID first = env->GetFieldID(pairClass, "first", "Ljava/lang/Object;");
jfieldID second = env->GetFieldID(pairClass, "second", "Ljava/lang/Object;");
return std::pair(env->GetObjectField(p, first), env->GetObjectField(p, second));
}
JNIEnv *env;
jobject pair;
auto [f1, f2] = extract_pair(env, pair);
auto [f11, f12] = extract_pair(env, f1);
auto [f21, f22] = extract_pair(env, f2);
std::pair p(
std::pair(extract_float(env, f11), extract_float(env, f12)),
std::pair(extract_float(env, f21), extract_float(env, f22)));
Though, I think I must ask, do you actually need to do this? Can you preprocess the nested pairs to something nicer on the Java side? Doing it on this side is ugly.
I am not able to cast back the user self defined class return from C++ jni callback
code snippet as follow:
//Kotlin class
data class class_record(var id:String?,var class_name:String?,var class_type)
// com.example.MainActivity
public native Object Cls(String id);
Vector vec_ classrecord=new Vector();
vec_classrecord=(Vector)Cls("1234");
// c++ jni code
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_MainActivity_Cls(JNIEnv *env, jobject instance, jstring id,
) {
jclass java_vector_class;
jmethodID java_vector_method;
jobject java_vector_object ;
auto vec_record=// call c++ method that return vector for class record pointer
jstring jni_str;
jclass javaClassRef;
// jni for java.util.Vector
java_vector_class = env->FindClass("java/util/Vector");
java_vector_method_constructor = env->GetMethodID(java_vector_class, "<init>", "()V");
java_vector_object = env->NewObject(java_vector_class, java_vector_method_constructor, "");
for (auto record_it = vec_record.begin(); record_it < vec_record.end(); ++record_it) {
// jni for class_record
jclass java_class = env->FindClass("com/example/class_record");
javaClassRef = (jclass) env->NewGlobalRef(java_class);
jmethodID cls_constructor = env->GetMethodID(javaClassRef, "<init>", "()V");
jobject cls_object = env->NewObject(javaClassRef, cls_constructor, "");
// set id
javaMethodRef = env->GetMethodID(javaClassRef, "setId", "(Ljava/lang/String;)V");
std::string strval = record_it.id;
jni_str = env->NewStringUTF(strval.c_str());
env->CallVoidMethod(cls_object, javaMethodRef, jni_str);
// set class_name
javaMethodRef = env->GetMethodID(javaClassRef, "setClass_name","(Ljava/lang/String;)V");
std::string strval = record_it.class_name;
jni_str = env->NewStringUTF(strval.c_str());
env->CallVoidMethod(cls_object, javaMethodRef, jni_str);
//set class_type
javaMethodRef = env->GetMethodID(javaClassRef, "setClass_type","(Ljava/lang/String;)V");
std::string strval = record_it.class_type;
jni_str = env->NewStringUTF(strval.c_str());
env->CallVoidMethod(cls_object, javaMethodRef, jni_str);
jmethodID java_vector_add = env->GetMethodID(java_vector_class, "addElement","(Ljava/lang/Object;)V");
**env->CallVoidMethod(java_vector_object, java_vector_add, javaClassRef);**
}
return java_vector_object;
}
env->CallVoidMethod(java_vector_object, java_vector_add, cls_object);
Under Kotlin environment , it it much better to express jni c++ callback in ArrayList instead of vector which grow almost double the size with snippet as follow in situation where the returned arraylist is fixed size immuatable.
java_util_class = env->FindClass("java/util/ArrayList");
jmethodID java_add= env->GetMethodID(java_util_class, "add","(Ljava/lang/Object;)Z");
env->CallBooleanMethod(java_object, java_add, cls_object);
sample tutorial
I have a JNI function in C which is getting passed in a Java FILE class which represents a directory listing. I would like to call the list() function and get the list of strings (files in the directory). What is the best way to do this?
Right now I have
static void* my_function(JNIEnv *env, jobject obj, jobject dir){
jarray listRet;
jclass cls = (*env)->GetObjectClass(env, dir);
jmethodID method = (*env)->GetMethodID(env, cls, "list", "()[Ljava/lang/String");
listRet = (*env)->CallObjectMethod(env, cls, method);
jsize stringCount = (*env)->GetArrayLength(env, listRet);
}
However, by adding logging statements, it seems that it never gets past the GetObjectClass call.
So, is this call correct? Further, is the GetMethodID call correct? The return type of list() is a (java) String[].
Is there anywhere else that I'm going wrong?
list is not a static method of File. That is, it belongs to an instance of File (dir in your case), not to the File class.
So instead of:
listRet = (*env)->CallObjectMethod(env, cls, method);
you should be using:
listRet = (*env)->CallObjectMethod(env, dir, method);
Also, you seem to be missing a semicolon in the signature for list. It should be "()[Ljava/lang/String;"
I'm trying to call a non static java method from C++ using the JNI on an Android project, but GetMethodID always return NULL.
this is my code:
void Java_com_kungfu_rabbit_KungFuRabbitActivity_nativeOnCreate(JNIEnv *env, jobject obj)
{
// this code works fine
jclass cls = env->FindClass("com/kungfu/rabbit/KungFuRabbitActivity");
jmethodID mid = env->GetStaticMethodID(cls, "foo", "()V");
env->CallStaticVoidMethod(cls, mid);
// this one fails:
jclass cls = env->GetObjectClass(obj);
jmethodID mid = env->GetMethodID(cls, "foo2", "()V");
env->CallVoidMethod(obj, mid);
}
I'm calling this native function from the class that extends Activity. foo is a public static void function, and foo2 is a public void function.
I can't understand why it fails...
Can anyone help me to understand?
Thanks in advance