Android JNI call Java method from native thread - android

I am trying to call a method in my java class from within a thread in the native code but not having any success. These are the global vars:
JavaVM* javaVM = NULL;
jclass activityClass;
jobject activityObj;
Code called on initialisation of the native code:
extern "C" {
JNIEXPORT jint JNICALL
naInit(JNIEnv *pEnv, jobject pObj, jstring pFileName, jstring, defaultStorageDirectory) {
pEnv->GetJavaVM(&javaVM);
jclass cls = pEnv->GetObjectClass(pObj);
activityClass = reinterpret_cast<jclass>((jclass) pEnv->NewGlobalRef(cls));
activityObj = pEnv->NewGlobalRef(pObj);
}
}
Code used within the thread function:
void *decodeAndRender(void * args) {
JNIEnv *env;
javaVM->AttachCurrentThread(&env, NULL);
jmethodID retryStartVideoMethodID = env->GetMethodID(activityClass, "retryStartVideo", "()V");
env->CallVoidMethod(activityObj, retryStartVideoMethodID);
javaVM->DetachCurrentThread();
return 0;
}
Java code :
public void retryStartVideo() {
Log.d(TAG, "METHOD CALLED FROM CPP ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
Error in logcat:
JNI DETECTED ERROR IN APPLICATION: JNI CallVoidMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Ljava/lang/Class;.retryStartVideo()V"
I have set the instances of the calling class to be a global ref and used that when calling from within the thread function but it is still failing to find the method. I can use similar code to call a static method no problem but I need to be able to call a non static one.

Related

How to call JNI Function from a method in C library

I'm currently developping an android app with android studio, for this project i need to use a custom library written in c/c++ .
So i in order to do that i needed to use NDK.
The library contain methods that i need to implements in order to have access to specifics fonctions in android
My question is : how can I call my jni method inside the method existing in c
which will call a java method to store session key in the android system
So for a pratical exemple since it's maybe not clear:
In a file called lib_exemple.c i have three methods
the first one is the initialisation call to the library (jni to library c) -->working
JNIEXPORT void JNICALL
Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1init
(JNIEnv *env, jobject instance) {
lib_Example_init('0'); // c func of the library
}
then the second one is the jni who call a java method (jni to java) -> working
JNIEXPORT jint
JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
(JNIEnv * env, jobject jobject1, jbyte jbyte1, jchar jchar1, jbyte jbyte2){
jclass clazz = (*env)->FindClass(env, "com/example/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*env)->GetMethodID(env, clazz, "KeyStoreSessionKey", "(BCB)I");
jint result = (*env)->CallIntMethod(env, jobject1, mCurrentActivityId, jbyte1, jchar1, jbyte2);
return result;
}
and the third method is the one the library c have in it (library C to jni)
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
//i want to call jni func here , so the librairy can access the native android function
return 0;
}
then to configure the ndk in a file called ExempleLib.java i have defined
static {
System.loadLibrary("LibExemple");
}
then the prototype of the initialisation of the library
public native void libExemple_init();
the prototype of the first method in lib_exemple.c
public native int lib_Exemple_store_session_key(byte pKey, char key_length, byte keyIndex);
and the java fonction called by it
protected int KeyStoreSessionKey(byte pKey, char key_length, byte keyIndex) {
...
}
my mainActivity contain
ExempleLib LibFunc = new ExempleLib();
Log.d(TAG, "init lib" );
LibFunc.lib_Exemple_init();
My goal is that after initialising my library and call an init function , it can call the first method in the second method of lib_exemple.c (the one used in the library )
thanks
EDIT :
The solution of my problem is when i initialize the lib , i need to save jvm
so i can access the JNIenv in the library method directly
jobject Savedinstance = NULL;
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
return JNI_VERSION_1_6;
}
JNIEnv *AttachJava() {
JavaVMAttachArgs args = {JNI_VERSION_1_6, 0, 0};
JNIEnv *java;
(*cachedJVM)->AttachCurrentThread(cachedJVM, &java, &args);
return java;
}
JNIEXPORT jint JNICALL
Java_com_example_vgosselin_libExemple_Libs_ExempleLib_lib_1init(JNIEnv *env,
jobject instance) {
Savedinstance = instance;
void *element = 0;
jint result;
result = lib_Exemple_init_();
return result;
}
so i can call the java method in
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
JNIEnv *NewEnv = AttachJava();
jclass clazz = (*NewEnv)->FindClass(NewEnv,"com/example/vgosselin/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*NewEnv)->GetMethodID(NewEnv,clazz,"KeystoreStoreKey","([BCB)I");
jint result;
jbyteArray Data = 0;
jchar Key = 0;
jbyte Type = 0;
result = (*NewEnv)->CallIntMethod(NewEnv, Savedinstance, mCurrentActivityId, Data, Key, Type);
return result;
}
and now i don't need the method in jni syntax anymore
JNIEXPORT jint JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key

JNI call to find Java class from native results in NoCLassDefFoundError

I am using following code to access a java class from native code
JNIEnv *env = nullptr;
JMVEnv::attachCurrentJNIENv(&env);
jclass jXYZClass = env->FindClass("com/xxx/xx/xx/XYZClassName");
This call passes 4-5 times but fails after that. jXYZClass is null after some calls.
So, while compilation class is found and during execution also it was found 4-5 times. I am calling this code from different locations. Can it be some threading issue?
I think it is a multhreading problems... Especially when you say that " it was found 4-5 times. I am calling this code from different locations".
See this post for more details.
Firstly, you can print the thread id of each thread to see if they are the same thread.
You can Findclass in JNI_OnLoad:
jobject g_class;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
JavaVM *gJvm = pjvm; // cache the JavaVM pointer
JNIEnv *env= NULL;
env = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
jclass tmp = env->FindClass("com/xxx/xx/xx/XYZClassName");
g_class = env->NewGlobalRef(tmp);
}
whenever you want to use this class:
// extern jobject g_class; Add this Line if this is in another cpp file
jmethodID methodID = env->GetMethodID((jclass)g_class, "<init>", "()V");
jobject new_object = env->NewObject((jclass)g_class, methodID);

What is "jobject thiz" in JNI and what is it used for?

I am having a hard time finding an answer to this. But, what is "jboject thiz" used for in JNI function calls? For example:
jobjectArray Java_com_gnychis_awmon_Test( JNIEnv* env, jobject thiz ) {
I use env to allocate objects often, but I've never used thiz and I'm not sure what it is for. Just for knowledge purposes.
The following is a JNI wrapper function which has two parameters, and returns a primitive array of objects:
jobjectArray Java_com_gnychis_awmon_Test( JNIEnv* env, jobject thiz );
From the function name you have given I don't think it is complete, that is, you haven't respected the obligatory function name convention which is:
Start the function with Java_
Append the package name separated by _ (undescores) i.e. com_company_awesomeapp. So far the function name is composed of: Java_com_company_awesomeapp
Append the Java class name where the native method has been defined,
followed by the actual function name. So at this point we should have the following function name: Java_com_company_awesomeapp_MainActivity_Test
The first parameter is a pointer to a structure storing all JNI function pointers, i.e. all the predefined functions you have available after you #include <jni.h>.
The second parameter is a reference to the Java object inside which this native method has been declared in. You can use it to call the other methods of the Java object from the current JNI function, i.e. Call Java instance methods from JNI code written in C or C++.
If for example you have the following Java class inside the MainActivity.java file:
public class MainActivity extends Activity
{
static
{
try
{
System.loadLibrary("mynativelib");
}
catch (UnsatisfiedLinkError ule)
{
Log.e(TAG, "WARNING: Could not load native library: " + ule.getMessage());
}
}
public static native Object[] Test();
}
Then, the jobject thiz parameter of the JNI function would be a reference to an object of type MainActivity.
I found this link that should help clarify the question.
https://library.vuforia.com/articles/Solution/How-To-Communicate-Between-Java-and-C-using-the-JNI
Here is an example in it that uses the "jobject".
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative(
JNIEnv* env, jobject obj, jint width, jint height)
{
...
jclass activityClass = env->GetObjectClass(obj);
jmethodID getTextureCountMethodID = env->GetMethodID(activityClass,
"getTextureCount", "()I");
if (getTextureCountMethodID == 0)
{
LOG("Function getTextureCount() not found.");
return;
}
textureCount = env->CallIntMethod(obj, getTextureCountMethodID);
...
}
jobject thiz means the this in java class.
Sometimes if you create a static native method like this.
void Java_MyClass_method1 (JNIEnv *, jclass);
jclass means the class itself.

JNI call fail on non static methods

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

JNI calling a Java Method from C++

I am having a problem with JNI, calling a method from C++ to Java.
I am trying to call a void method that takes a boolean. My java code is the following:
public void setStatus(boolean bool) {
// Do stuff...
}
public native void initialize(int defaultPort);
In my C++ code, I am making a struct to hold the env and object and pass it to a thread:
JNIEXPORT void JNICALL Java_com_device_client_HostConnection_initialize
(JNIEnv * env, jobject obj, jint port)
{
struct javaInfo* data = (struct javaInfo*) malloc(sizeof(struct javaInfo));
data->env = env;
data->javaObjHost = obj;
pthread_t pth;
pthread_create(&pth, NULL, startServer, (void *) data);
free(data);
}
In the actual function, I am trying to obtain the class and then the MethodID and then call the void method, as follows:
void *startServer(void* arg) {
struct javaInfo* data = (struct javaInfo*) arg;
JNIEnv* env = data->env;
jobject javaObjHost = data->javaObjHost;
cls = env->GetObjectClass(javaObjHost);
mid = env->GetMethodID(cls, "setStatus", "(Z)V");
if (mid == 0) {
exit(-1);
}
env->CallVoidMethod(javaObjHost, mid, true);
}
It is hard for me to debug with JNI. I have tried putting a breakpoint in Eclipse in setStatus() but it never gets called. exit() is not called as well. The programs stomps for a second or two, then continues. I am not sure what is going on.
Could anyone please help me?
Thank you very much.
You cannot pass env pointers to other threads. You need to join the thread to the JVM.
In the original thread, called GetJavaVM to obtain a JavaVM pointer:
JavaVM *vm = 0;
env->GetJavaVM(&vm);
Then in the other thread, attach the VM to that thread and get a new env pointer:
vm->AttachCurrentThread(&env, 0);

Categories

Resources