简体   繁体   中英

JNI: Calling a java method from C periodically is not working

I am calling a java method from JNI layer periodically (every 50ms) using timer_create. My Java method ( callback() ) gets called for sometime but after that its not getting called and my application hangs. If I touch anything on the screen I get ANR.

To check whether it's a timer issue or JNI call (calling java method), I commented everything inside handler() except log statement. I observed that log is getting printed continuously, which made me conclude that problem is in calling java method from handler().

I am not sure on which thread(UI or non-UI) this JNI call is made when I use AttachCurrentThread() . If its made on UI thread then please let me know how to make it run on non-UI thread. Or is there any other problem in my code which is causing it.

If you observe the output, native and java methods are called continuously but after sometime only native method is called and after few calls that too gets stopped.

/******************Native Code**************************/

void handler(int sig, siginfo_t *si, void *uc) {
    JNIEnv * g_env;

    __android_log_print(ANDROID_LOG_INFO, TAG, "Native handler"); 

    int getEnvStat = (*g_vm)->GetEnv(g_vm,(void **)&g_env, JNI_VERSION_1_6);

    if (getEnvStat == JNI_EDETACHED) {

        if ((*g_vm)->AttachCurrentThread(g_vm, (void **) &g_env, NULL) != 0) {

        }
    } else if (getEnvStat == JNI_OK) {

    } else if (getEnvStat == JNI_EVERSION) {

    }

    (*g_env)->CallVoidMethod(g_env,g_obj, g_mid);

    if ((*g_env)->ExceptionCheck(g_env)) {
        (*g_env)->ExceptionDescribe(g_env);
    }
}

void initTimer() {

    struct new_value;
    struct sigaction action;
    struct sigevent sev;
    timer_t timerid;

    /* Establish handler for timer signal */
    action.sa_flags = SA_SIGINFO;
    action.sa_sigaction = handler;
    sigemptyset(&action.sa_mask);
    if (sigaction(SIG1, &action, NULL) == -1)
        __android_log_print(ANDROID_LOG_INFO, TAG, "sigaction");

    /* Create the timer */
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIG1;
    sev.sigev_value.sival_ptr = &timerid;
    if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1)
        __android_log_print(ANDROID_LOG_INFO, TAG, "timer_create");

    /* Start the timer */
    new_value.it_interval.tv_sec = 0;
    new_value.it_interval.tv_nsec = 50*1000000; /* 50 ms*/
    new_value.it_value.tv_sec = 0;
    new_value.it_value.tv_nsec = 50*1000000; /* 50 ms */
    if (timer_settime(timerid, 0, &new_value, NULL) == -1)
        __android_log_print(ANDROID_LOG_INFO, TAG, "timer_settime");
}

JNIEXPORT void JNICALL Java_com_foo_MyJavaClass_register
        (JNIEnv * env, jobject obj, jint delay) {


    // convert local to global reference
    // local will die after this method call
    g_obj = (*env)->NewGlobalRef(env, obj);

    // save refs for callback
    jclass g_clazz = (*env)->GetObjectClass(env, g_obj);
    if (g_clazz == NULL) {

    }

    g_mid = (*env)->GetMethodID(env, g_clazz, "callback", "()V");
    if (g_mid == NULL) {

    }

    initTimer();

}

/***Java callback **/

public class MyJavaClass {

    public void callback() {

        Log.e("", "Java callback " );

    }

    public native void register(int delayInMs);

}

/***Output log****/

09-06 05:00:45.430: I/(31763): Native handler
09-06 05:00:45.430: E/(31763): Java callback : : : 09-06 05:00:45.480: I/(31763): Native handler
09-06 05:00:45.480: E/(31763): Java callback 09-06 05:00:45.520: I/(31763): Native handler
09-06 05:00:45.520: E/(31763): Java callback 09-06 05:00:45.570: I/(31763): Native handler
09-06 05:00:45.570: E/(31763): Java callback 09-06 05:00:45.620: I/(31763): Native handler
09-06 05:00:45.620: E/(31763): Java callback 09-06 05:00:45.680: I/(31763): Native handler
09-06 05:00:45.680: E/(31763): Java callback 09-06 05:00:45.720: I/(31763): Native handler
09-06 05:00:45.770: I/(31763): Native handler
09-06 05:00:45.840: I/(31763): Native handler
09-06 05:00:45.880: I/(31763): Native handler
09-06 05:00:45.930: I/(31763): Native handler 09-06 05:00:45.970: I/(31763): Native handler
09-06 05:00:46.030: I/(31763): Native handler
09-06 05:00:46.070: I/(31763): Native handler
09-06 05:00:46.130: I/(31763): Native handler
09-06 05:00:46.180: I/(31763): Native handler
09-06 05:00:46.230: I/(31763): Native handler
09-06 05:00:46.270: I/(31763): Native handler
09-06 05:00:46.330: I/(31763): Native handler
09-06 05:00:46.370: I/(31763): Native handler

Any idea on how to resolve this issue? Thanks in advance !

Andrew's comment is correct. It is a bad idea to call JVM from a signal handler. There is no control what the JVM does and signal handler must be async-signal-safe. So what to do? There are 2 options in general:

Option 1

Use SIGEV_THREAD instead of SIGEV_SIGNAL`. Each timer tick creates a new thread and executes it. It might be a performance bottleneck for fast timers.

struct sigevent sev;
timer_t timerid;
memset(&sev, 0, sizeof(sev));
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = handler;
sev.sigev_value.sival_ptr = &timerid;
timer_create(CLOCK_MONOTONIC, &sev, &timerid);

The timer handler is running always in a new thread so the handler must always attach and detach JVM.

Option 2

Change the design of your application. Start a new native thread that will wait for a flag in an infinite loop. The flag can be set inside the signal handler. When the flag is set then the native thread wakes up and call JNI, and then start waiting for another flag set. You can use a semaphore to implement the flag. Note the sem_post is async-signal-safe.

Somehow JNI call was hampering the main process. So I created a service in a different process and loaded my native lib from this service. Whenever native timer expires I would send this event from service to main process using AIDL. Its working now but I still need to test it thoroughly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM