Android 和 JNI 实时时钟
Android and JNI real time clock
我在使用迷你 Android 应用程序以及在 C (JNI) 函数中使用实时时钟信号时遇到问题。
似乎 Android UI
不喜欢来自 C 函数中实例化的定时器的实时信号。
在下面的 PoC
中,计时器每秒触发信号 5 次,如果在 UI 更新时触发信号,应用程序会崩溃。
- 如果我不启动计时器 => 不会崩溃
- 如果我不放任何东西 UI => 不会崩溃
我写了这个小 PoC 来证明这种行为。 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()
函数。
#include <jni.h>
#include <android/log.h>
#include <signal.h>
#include <time.h>
#include <strings.h>
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);
}
有时它会立即消失,有时它会在我按下按钮后消失(我认为这取决于 signal/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
我也尝试更改信号编号(从 SIGRTMIN
到 SIGRTMIN + 20
)但没有成功....
我的问题是....是否可以在不破坏 RenderThread 的情况下在 JNI 函数中使用实时时钟信号? (是的,是后者崩溃了)
在 JNI 函数中玩定时器是个坏习惯吗?
可能是定时器信号被传送到渲染器线程或主线程。在这种情况下,它会中断该线程中正在进行的系统调用(如果有的话)。而这种情况可能会在运行时代码中触发一些断言。您可以使用 SIGEV_THREAD_ID
将信号定向到专用线程。但请注意,此通知类型不适用于广泛使用。此外,一些实时信号可能会被仿生线程实现甚至 ART 悄悄地使用。所以很容易坏东西。
P.S。如果可能 - 你应该更喜欢 SIGEV_THREAD
。它在 Android 上看起来更可靠,因为你不应该考虑正确的信号编号等。
我在使用迷你 Android 应用程序以及在 C (JNI) 函数中使用实时时钟信号时遇到问题。
似乎 Android UI
不喜欢来自 C 函数中实例化的定时器的实时信号。
在下面的 PoC
中,计时器每秒触发信号 5 次,如果在 UI 更新时触发信号,应用程序会崩溃。
- 如果我不启动计时器 => 不会崩溃
- 如果我不放任何东西 UI => 不会崩溃
我写了这个小 PoC 来证明这种行为。 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()
函数。
#include <jni.h>
#include <android/log.h>
#include <signal.h>
#include <time.h>
#include <strings.h>
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);
}
有时它会立即消失,有时它会在我按下按钮后消失(我认为这取决于 signal/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
我也尝试更改信号编号(从 SIGRTMIN
到 SIGRTMIN + 20
)但没有成功....
我的问题是....是否可以在不破坏 RenderThread 的情况下在 JNI 函数中使用实时时钟信号? (是的,是后者崩溃了)
在 JNI 函数中玩定时器是个坏习惯吗?
可能是定时器信号被传送到渲染器线程或主线程。在这种情况下,它会中断该线程中正在进行的系统调用(如果有的话)。而这种情况可能会在运行时代码中触发一些断言。您可以使用 SIGEV_THREAD_ID
将信号定向到专用线程。但请注意,此通知类型不适用于广泛使用。此外,一些实时信号可能会被仿生线程实现甚至 ART 悄悄地使用。所以很容易坏东西。
P.S。如果可能 - 你应该更喜欢 SIGEV_THREAD
。它在 Android 上看起来更可靠,因为你不应该考虑正确的信号编号等。