Android和JNI实时时钟

我遇到了迷你Android应用程序的问题以及在C(JNI)函数中使用实时时钟信号。

看起来Android UI不喜欢来自C函数中实例化的定时器的实时信号。

在下面的PoC ,定时器每秒触发信号5次,如果在UI更新时触发信号,则应用程序崩溃。

  • 如果我没有启动计时器=>没有崩溃
  • 如果我没有在UI上放任何东西=>没有崩溃

我写了这个小PoC来certificate这种行为。 Java部分只是调用JNI函数并在屏幕上放置一个按钮。

 public class MainActivity extends AppCompatActivity { Button bt; static { System.loadLibrary("testtimer-jni"); } /* JNI ingresso */ public native void jniStartTimer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); jniStartTimer(); /* load button */ bt = new Button(getBaseContext()); setContentView(bt); } } 

这是main.c文件的内容。 实例化并启动计时器。 每200ms (每秒5次cb()调用cb()函数。

 #include  #include  #include  #include  #include  timer_t timer_id = 0x12; struct itimerspec timer; struct sigevent te; struct sigaction sa; void cb(int sig, siginfo_t *si, void *uc) { __android_log_write(ANDROID_LOG_ERROR, "Test", "Called callback"); } void Java_it_dbtecno_testtimer_MainActivity_jniStartTimer(JNIEnv *env, jobject thiz) { __android_log_write(ANDROID_LOG_ERROR, "Test", "Timer inited"); /* prepare timer to emulate video refresh interrupts */ sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = cb; sigemptyset(&sa.sa_mask); if (sigaction(SIGRTMIN + 7, &sa, NULL) == -1) return; bzero(&te, sizeof(struct sigevent)); /* set and enable alarm */ te.sigev_notify = SIGEV_SIGNAL; te.sigev_signo = SIGRTMIN + 7; te.sigev_value.sival_ptr = &timer_id; timer_create(CLOCK_REALTIME, &te, &timer_id); timer.it_value.tv_sec = 1; timer.it_value.tv_nsec = 1000; timer.it_interval.tv_sec = 0; timer.it_interval.tv_nsec = 1000000000 / 5; /* start timer */ timer_settime(timer_id, 0, &timer, NULL); } 

有时它会立即死亡,有时它会在我按下按钮后死亡(我认为这取决于信号/ UI更新的时间)并且它会在日志中输出

 09-22 11:52:12.087 13587-13587/it.dbtecno.testtimer I/Test: Called callback 09-22 11:52:12.288 13587-13587/it.dbtecno.testtimer I/Test: Called callback 09-22 11:52:12.501 13587-13587/it.dbtecno.testtimer I/Test: Called callback 09-22 11:52:12.532 13587-13587/it.dbtecno.testtimer A/OpenGLRenderer: Task is already in the queue! 09-22 11:52:12.532 13587-13587/it.dbtecno.testtimer A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 13587 (tecno.testtimer) 09-22 11:52:12.637 1187-1187/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 09-22 11:52:12.637 1187-1187/? A/DEBUG: Build fingerprint: 'Android/sdk_google_phone_x86/generic_x86:6.0/MASTER/3079352:userdebug/test-keys' 09-22 11:52:12.637 1187-1187/? A/DEBUG: Revision: '0' 09-22 11:52:12.637 1187-1187/? A/DEBUG: ABI: 'x86' 09-22 11:52:12.638 1187-1187/? A/DEBUG: pid: 13587, tid: 13587, name: tecno.testtimer >>> it.dbtecno.testtimer <<< 09-22 11:52:12.638 1187-1187/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- 09-22 11:52:12.642 1187-1187/? A/DEBUG: Abort message: 'Task is already in the queue!' 09-22 11:52:12.642 1187-1187/? A/DEBUG: eax 00000000 ebx 00003513 ecx 00003513 edx 00000006 09-22 11:52:12.642 1187-1187/? A/DEBUG: esi b77a5c50 edi 0000000b 09-22 11:52:12.642 1187-1187/? A/DEBUG: xcs 00000073 xds 0000007b xes 0000007b xfs 00000007 xss 0000007b 09-22 11:52:12.642 1187-1187/? A/DEBUG: eip b736f666 ebp 00003513 esp bfdc7540 flags 00200202 09-22 11:52:12.644 1187-1187/? A/DEBUG: backtrace: 09-22 11:52:12.645 1187-1187/? A/DEBUG: #00 pc 00084666 /system/lib/libc.so (tgkill+22) 09-22 11:52:12.650 1187-1187/? A/DEBUG: #01 pc 00081608 /system/lib/libc.so (pthread_kill+70) 09-22 11:52:12.651 1187-1187/? A/DEBUG: #02 pc 00027205 /system/lib/libc.so (raise+36) 09-22 11:52:12.651 1187-1187/? A/DEBUG: #03 pc 000209e4 /system/lib/libc.so (abort+80) 09-22 11:52:12.659 1187-1187/? A/DEBUG: #04 pc 0000cbc3 /system/lib/libcutils.so (__android_log_assert+128) 09-22 11:52:12.660 1187-1187/? A/DEBUG: #05 pc 00025e81 /system/lib/libhwui.so (android::uirenderer::renderthread::RenderThread::queue(android::uirenderer::renderthread::RenderTask*)+81) 09-22 11:52:12.660 1187-1187/? A/DEBUG: #06 pc 00021b44 /system/lib/libhwui.so 09-22 11:52:12.660 1187-1187/? A/DEBUG: #07 pc 000243e5 /system/lib/libhwui.so (android::uirenderer::renderthread::RenderProxy::syncAndDrawFrame()+29) 09-22 11:52:12.660 1187-1187/? A/DEBUG: #08 pc 000ba75b /system/lib/libandroid_runtime.so 09-22 11:52:12.660 1187-1187/? A/DEBUG: #09 pc 72dfe20e /data/dalvik-cache/x86/system@framework@boot.oat (offset 0x1eb2000) 09-22 11:52:12.713 1187-1187/? A/DEBUG: Tombstone written to: /data/tombstones/tombstone_08 09-22 11:52:12.713 1187-1187/? E/DEBUG: AM write failed: Broken pipe 

我也尝试改变信号编号(从SIGRTMINSIGRTMIN + 20 )但没有运气….

我的问题是……是否可以在JNI函数中使用实时时钟信号而不会破坏RenderThread? (是的,后者崩溃了)

在JNI函数中与计时器一起玩是一个坏习惯吗?

可能会将定时器信号传递给渲染器线程或主线程。 在这种情况下,它会中断该线程中正在进行的系统调用(如果有)。 这种情况可能会在运行时代码中触发一些断言。 您可以使用SIGEV_THREAD_ID将信号指向专用线程。 但请注意,此通知类型不适合广泛使用。 此外,仿生线程实现甚至ART都可以静默使用一些实时信号。 所以很容易打破一些东西。

PS如果可能 – 你应该更喜欢SIGEV_THREAD 。 它在Android上看起来更可靠,因为你不应该考虑正确的信号数字等。