如何从C ++中的单独线程发布要在Android主线程上运行的代码?

时间:2015-12-10 21:27:46

标签: android android-ndk

我有一个单独的线程在后台用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 ++中。

5 个答案:

答案 0 :(得分:3)

一个线程只能有一个与之关联的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 显示 second 。当从不同的线程发出两个 post ,或者延迟时,肯定没有任何结论。

你可以在一个伟大的Android messaging and concurrency framework for native code development找到一个blog post desctibed。

更新

这是必需的证明。在处理不相关的问题时收到了下面的堆栈跟踪:

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)

答案 1 :(得分:1)

您需要在主线程中执行的功能。如果你在那里调用ALooper_forThread()ALooper_prepare(),你将获得一个指向与主线程关联的looper的指针。请记住调用ALooper_acquire(),以便可以在不同的线程之间共享。

答案 2 :(得分:0)

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

但是通过java端的处理程序很容易实现,通过jni调用发送和处理在本机和java之间来回传递的消息。

答案 3 :(得分:0)

您必须通过Java,因为 android.os.Looper未在原生代码中实现(至少在the currently most recent commit中)。

我没有足够的NDK经验来快速输入所需的样板,但显而易见的选择似乎是基于本机代码创建一个java Runnable并将其发送给looper。

不那么明显的解决方案是直接在线程的MessageQueue上运行。一旦你有了它的引用,你可以在那里注册本机管道的一端,并将消息写入另一端;管道基本上采用Handler的功能,但在本机代码上。从技术上讲,您的代码仍然是从Java调用的,但您不需要开销。我没有找到关于整个事情的大量文档,但this thread可能是一个很好的起点.¹

但是,完全有可能实际上不必从主线程调用您的代码,或者有其他选项可以在不通过Java的情况下解决您的问题。然而,这将取决于您试图解决的问题。

注意:我假设主线程场景。如果您可以在要部署的线程中使用基于本机代码的循环器,则可以有更多选项。

¹有可能ALooper可以在某种客户端模式下使用它来执行此操作。对此非常不确定。

答案 4 :(得分:0)

如果你想从另一个线程在主线程中创建一些东西,我建议你使用 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

我希望它有所帮助。