2016-12-10 7 views
1

Я вызываю Java-метод из уровня JNI периодически (каждые 50 мс), используя timer_create. Мой метод Java (callback()) вызывается в течение некоторого времени, но после этого его вызов не будет вызван, и мое приложение зависает. Если я прикасаюсь к чему-либо на экране, я получаю ANR.JNI: Вызов метода Java из C периодически не работает

Чтобы проверить, не является ли проблема с таймером или вызовом JNI (вызов метода java), я прокомментировал все внутри обработчика(), за исключением оператора журнала. Я заметил, что журнал печатается непрерывно, что заставило меня прийти к выводу, что проблема заключается в вызове java-метода из обработчика().

Я не уверен, какой поток (UI или не-UI) этот вызов JNI выполняется, когда я использую AttachCurrentThread(). Если это сделано в потоке пользовательского интерфейса, то, пожалуйста, дайте мне знать, как заставить его работать на потоке, отличном от UI. Или есть другие проблемы в моем коде, которые вызывают это.

Если вы наблюдаете выходные данные, методы native и java вызываются непрерывно, но через некоторое время вызывается только собственный метод и после нескольких вызовов, которые тоже останавливаются.

/******************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); 

} 

/*** Журнал Выходной ****/

09-06 05: 00: 45,430: I/(31763): Native обработчик
09-06 05: 00: 45,430: E/(31763): Java обратного вызова : : : 09-06 05: 00: 45,480: I/(31763): Native обработчик
09-06 05: 00: 45,480: E/(31763): Java обратный вызов 09-06 05: 00: 45.520: I/(31763): Родной обработчик
09-06 05: 00: 45.520: E/(31763): Обратный вызов Java 09-06 05: 00: 45.570: I/(31763): Родной обработчик
09-06 05: 00: 45.570: E/(31763): Обратный вызов Java 09-06 05: 00: 45.620: I/31763): Родной обработчик
09-06 05: 00: 45.620: E/(31763): Обратный вызов Java 09-06 05: 00: 45.680: I/(31763): Родной обработчик
09-06 05:00 : 45.680: E/(31763): Java callback 09-06 05: 00: 45.720: I/(31763): родной обработчик
09-06 05: 00: 45.770: I/(31763): родной обработчик
09-06 05: 00: 45.840: I/(31763): Родной обработчик
09-06 05: 00: 45.880: I/(31763): Родной обработчик
09-06 05: 00: 45.930: I/(31763): Родной обработчик 09-06 05: 00: 45.970: I/(31763): Собственный обработчик
09-06 05: 00: 46.030: I/31763): Родной обработчик
09-06 05: 00: 46,070: I/(31763): Native обработчик
09-06 05: 00: 46,130: I/(31763): Native обработчик
09-06 05: 00: 46.180: I/(31763): Родной обработчик
09-06 05: 00: 46.230: I/(31763): Родной обработчик
09-06 05: 00: 46.270: I/(31763): Нативный обработчик
09-06 05: 00: 46.330: I/(31763): родной обработчик
09-06 05: 00: 46.370: I/(31763): родной обработчик

Любая идея о том, как решить эту проблему? Спасибо заранее!

+0

Из [Советы JNI от Google] (https://developer.android.com/training/articles/perf-jni.html): «Прикрепленные темы [с помощью' AttachCurrentThread'] ** должны вызывать ** 'DetachCurrentThread' * * до выхода **. " – Michael

+2

Вы действительно вызываете методы Java из обработчика сигнала * *? ** Только ** [асинхронные сигнальные вызовы функций] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04) можно безопасно сделать из обработчика сигнала. Все остальное, скорее всего, приведет к неопределенному поведению. –

+0

Да @ Andrew. Я использую _signal handler_ для вызова java-метода. Я хотел, чтобы java-метод назывался точно в 50 мс. Есть ли лучший способ достичь этого? Если я использую сон в _pthread_, я наблюдаю, что java-метод не вызывается с точным интервалом 50 мс. – Autumn

ответ

3

Комментарий Эндрю является правильным. Плохая идея вызывать JVM из обработчика сигналов.Нет никакого контроля над тем, что делает JVM, и обработчик сигналов должен быть безопасным для асинхронного сигнала. Так что делать? Есть 2 варианта в целом:

Вариант 1

Использование SIGEV_THREAD вместо SIGEV_SIGNAL`. Каждый таймер таймера создает новый поток и выполняет его. Это может быть узким местом производительности для быстрых таймеров.

Обработчик таймера работает всегда в новой резьбе, поэтому обработчик должен всегда прикреплять и отсоединять JVM.

Вариант 2

Изменить дизайн вашего приложения. Запустите новый собственный поток, который будет ждать флаг в бесконечном цикле. Флаг может быть установлен внутри обработчика сигнала. Когда флаг установлен, внутренний поток просыпается и вызывает JNI, а затем начинает ожидание установки другого флага. Вы можете использовать семафор для реализации флага. Обратите внимание: sem_post является безопасным для асинхронного сигнала.

+0

Вариант 2 работал для меня. Приветствия! – Autumn

+0

Я пробовал вариант 2 с моим тестовым приложением, у которого есть голые мини-вещи (одна активность и один класс java), и она отлично работала. Но когда я интегрировал это решение с моим фактическим приложением (имея много фрагментов и потоков), я снова начал сталкиваться с той же проблемой. Если я позволяю приложению работать без касания экрана, он работает 24 часа, но в тот момент, когда я начинаю играть с элементами управления пользовательского интерфейса (прокручивать и нажимать), он зависает. Извините за сообщение так поздно! Рождество !!! – Autumn

0

Почему-то вызов JNI мешал основному процессу. Поэтому я создал службу в другом процессе и загрузил свой родной lib из этой службы. Когда истечет срок действия основного таймера, я отправил это событие из службы в основной процесс, используя AIDL. Теперь он работает, но мне все равно нужно тщательно его протестировать.