2016-09-22 72 views
3

我遇到了一個迷你Android應用程序和使用實時時鐘信號在C(JNI)函數中的問題。Android和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); 
} 

有時它死亡瞬間,有時它死之後,我按下按鈕(我認爲這取決於信號/ 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/[email protected]@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),但沒有運氣....

我的問題是....是否有可能在不破壞RenderThread的情況下使用JNI函數中的實時時鐘信號? (是的,後者是崩潰)

在JNI函數中使用定時器玩壞它的習慣嗎?

回答

2

可能定時器信號被傳遞到渲染器線程或主線程。在這種情況下,它會中斷該線程正在進行的系統調用(如果有的話)。這種情況可能會觸發運行時代碼中的某些斷言。你可以玩SIGEV_THREAD_ID將信號引導至專用線程。但請注意,此通知類型不適用於廣泛使用。另外一些實時信號可能會被仿生線程實現甚至ART所默默使用。所以打破某些東西很容易。

P.S.如果可能的話 - 你應該更喜歡SIGEV_THREAD。它在Android上看起來更可靠,因爲您不應該考慮適當的信號編號等。

+0

Yess!它適用於SIGEV_THREAD ....你認爲我可以獲得與SIGEV_SIGNAL方法相同的性能級別嗎?我認爲每次定時器觸發時都會產生一個線程 –

+0

我不確定android是否在每次計時器觸發時都會產生新的線程,但是,無論如何,另一種通知方法或中斷應用程序完全或風險太大而無法使用。可悲的是,'SIGEV_THREAD'是你唯一可靠的選擇。 – Sergio