我有一个单独的线程在后台用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 ++中。
答案 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 } } }); }
无论如何,我建议您阅读以下链接:link1,link2,link3。
我希望它有所帮助。