簡體   English   中英

如何從C ++中的單獨線程發布要在Android主線程上運行的代碼?

[英]How do I post code to be run on the Android main thread from a separate thread in C++?

我有一個單獨的線程在后台運行C ++,我希望它能夠發布代碼在另一個已運行android.os.Looper的線程上運行(例如主線程)。 通過'post',我的意思是類似於View#post ,其中Runnable被排隊以在事件循環上運行。 將要執行的代碼也是用C ++編寫的。

我找到了ALooper API( http://developer.android.com/ndk/reference/group___looper.html ),但文檔不是很好,我不清楚是否將ALooper與目標線程關聯,添加另一個FD並發信號通知它將使我的代碼在事件隊列中相對於其他排隊的Runnables保持正確的排序。

我寧願不必通過Java並獲得Handler等等 - 這似乎是不必要的,因為我正在嘗試運行的代碼和發布它的代碼都是用c ++編寫的。

一個線程只能有一個與之關聯的Looper,一個Looper只有一個消息隊列,因此混合Java和本機回調將保持排序。

有了這個,我認為今天Android中沒有任何合同義務, post()保證按特定順序執行,即

getHandler().post(new Runnable() {
    @Override
    public void run() {
        mTextView.setText("first");
    }
});
getHandler().post(new Runnable() {
    @Override
    public void run() {
        mTextView.setText("second");
    }
});

沒有正式保證讓mTextView顯示第二個 當兩個帖子從不同的線程發出或延遲時,絕對沒有任何結論。

您可以在一篇精彩的博客文章中找到用於本機代碼開發Android消息傳遞和並發框架

更新

這是必需的證明。 在處理不相關的問題時收到了下面的堆棧跟蹤:

A/art: art/runtime/check_jni.cc:65]   native: #00 pc 0000484c  /system/lib/libbacktrace_libc++.so (UnwindCurrent::Unwind(unsigned int, ucontext*)+23)
A/art: art/runtime/check_jni.cc:65]   native: #01 pc 00003031  /system/lib/libbacktrace_libc++.so (Backtrace::Unwind(unsigned int, ucontext*)+8)
A/art: art/runtime/check_jni.cc:65]   native: #02 pc 002441f9  /system/lib/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, char const*, art::mirror::ArtMethod*)+68)
A/art: art/runtime/check_jni.cc:65]   native: #03 pc 002285a1  /system/lib/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&) const+144)
A/art: art/runtime/check_jni.cc:65]   native: #04 pc 000afe9b  /system/lib/libart.so (art::JniAbort(char const*, char const*)+582)
A/art: art/runtime/check_jni.cc:65]   native: #05 pc 000b05d1  /system/lib/libart.so (art::JniAbortF(char const*, char const*, ...)+60)
A/art: art/runtime/check_jni.cc:65]   native: #06 pc 000b299d  /system/lib/libart.so (art::ScopedCheck::Check(bool, char const*, ...) (.constprop.129)+672)
A/art: art/runtime/check_jni.cc:65]   native: #07 pc 000bab87  /system/lib/libart.so (art::CheckJNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+50)
A/art: art/runtime/check_jni.cc:65]   native: #08 pc 00060817  /system/lib/libandroid_runtime.so (???)
A/art: art/runtime/check_jni.cc:65]   native: #09 pc 000a5b29  /system/lib/libandroid_runtime.so (???)
A/art: art/runtime/check_jni.cc:65]   native: #10 pc 00010fd7  /system/lib/libutils.so (android::Looper::pollInner(int)+482)
A/art: art/runtime/check_jni.cc:65]   native: #11 pc 00011081  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+92)
A/art: art/runtime/check_jni.cc:65]   native: #12 pc 0007fbe5  /system/lib/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, int)+22)
A/art: art/runtime/check_jni.cc:65]   native: #13 pc 00051b8b  /system/framework/arm/boot.oat (Java_android_os_MessageQueue_nativePollOnce__JI+102)
A/art: art/runtime/check_jni.cc:65]   at android.os.MessageQueue.nativePollOnce(Native method)
A/art: art/runtime/check_jni.cc:65]   at android.os.MessageQueue.next(MessageQueue.java:143)
A/art: art/runtime/check_jni.cc:65]   at android.os.Looper.loop(Looper.java:122)
A/art: art/runtime/check_jni.cc:65]   at android.app.ActivityThread.main(ActivityThread.java:5411)
A/art: art/runtime/check_jni.cc:65]   at java.lang.reflect.Method.invoke!(Native method)
A/art: art/runtime/check_jni.cc:65]   at java.lang.reflect.Method.invoke(Method.java:372)
A/art: art/runtime/check_jni.cc:65]   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:916)
A/art: art/runtime/check_jni.cc:65]   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709)

您需要一個已在主線程中執行的函數。 如果你在那里調用ALooper_forThread()ALooper_prepare() ,你將獲得一個指向與主線程相關的looper的指針。 記得調用ALooper_acquire()以便它可以在不同的線程之間共享。

這可以幫助您https://groups.google.com/forum/#!topic/android-ndk/v2OITtaZTes

但是通過java端的處理程序很容易實現,通過jni調用發送和處理在本機和java之間來回傳遞的消息。

你將不得不通過Java,因為android.os.Looper沒有在本機代碼中實現 (至少在當前最近的提交中 )。

我沒有足夠的NDK經驗來快速輸入所需的樣板,但顯而易見的選擇似乎是創建一個基於本機代碼的java Runnable並將其發送到looper。

不太明顯的解決方案是直接在線程的MessageQueue上運行。 一旦你有了它的引用,你可以在那里注冊本機管道的一端,並將消息寫入另一端; 管道基本上具有Handler的功能,但在本機代碼上。 從技術上講,您的代碼仍然是從Java調用的,但您不需要開銷。 我沒有找到關於整個事情的大量文檔,但這個帖子可能是一個很好的起點.¹

但是,完全有可能實際上不必從主線程調用您的代碼,或者有其他選項可以在不通過Java的情況下解決您的問題。 然而,這將取決於您試圖解決的問題。


注意:我假設主線程場景。 如果您可以在要部署的線程中使用基於本機代碼的循環器,則可以使用更多選項。

¹也可以在某種客戶端模式下使用ALooper來實現這一點。 對此非常不確定。

如果你想從另一個線程在主線程中創建一些東西,我建議你使用runOnUiThread函數。 Android中的主要線程是用戶界面線程。 我不確定你是否可以在ndk代碼中使用這個功能。

代碼示例可能是這樣的:

private void runOnMainThread(){

 runOnUiThread(new Runnable(){ public void run() { try { // do some stuffs } catch (final Exception ex) { // handle the possible exception } } }); } 

無論如何,我建議你閱讀以下鏈接: link1link2link3

我希望它有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM