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

Exception or error:

I have a separate thread running in C++ in the background and I want it to be able to post code to be run on another thread that’s already running an android.os.Looper (e.g. the main thread). By ‘post’, I mean something akin to View#post where a Runnable is enqueued to be run on the event loop. The code that would be executed is also written in C++.

I found the ALooper API (http://developer.android.com/ndk/reference/group___looper.html), but the docs aren’t great and it’s unclear to me whether getting the ALooper associated with the destination thread, adding another FD, and signaling it will make my code maintain correct ordering in the event queue with respect to the other enqueued Runnables.

I’d prefer not to have to go through Java and get a Handler, etc. — it just seems unnecessary since both the code I’m trying to run and the code that’s posting it are in c++.

How to solve:

A thread can only have one Looper associated with it, a Looper has only one message queue, so mixing Java and native callbacks will maintain ordering.

With that, I don’t think there is any contractual obligation in Android today that post() is guaranteed to execute in particular order, i.e.

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

is not formally guaranteed to leave mTextView displaying second. Definitely nothing is set in stone when two posts are issued from different threads, or delayed.

You can find an Android messaging and concurrency framework for native code development desctibed in a great blog post.

Update

Here is the required proof. The stacktrace below was received while working on an unrelated problem:

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)

###

You need a function already executed in the main thread. If you call ALooper_forThread() or ALooper_prepare() there, you will get a pointer to looper associated with main thread. Remember to call ALooper_acquire() so it can be shared among different threads.

###

This could help you https://groups.google.com/forum/#!topic/android-ndk/v2OITtaZTes

But it is easy to achieve through a handler on the java side, sending and processing messages doing back and forth between native and java with jni calls.

###

If you want to make some stuffs in the main thread from another thread, I suggest you to use the runOnUiThread function. The main thread in Android is the User Interface thread. I’m not sure if you can use this function in the ndk code.

An example of code could be this:

private void runOnMainThread() {

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

Anyway I suggest you to read the follow links: link1, link2, link3.

I hope it helps.

###

You will have to go through Java, as android.os.Looper is not implemented in native code (at least in the currently most recent commit).

I have not enough experience with the NDK to quickly type the required boilerplate, but the obvious option seems to be creating a java Runnable based on native code and sending it to the looper.

The not-so-obvious solution is directly operating on the MessageQueue of the thread. Once you have a reference of that, you can register one end of a native pipe there, and write messages to the other end; the pipe basically takes the function of a Handler, but on native code. Technically, your code is still called from Java, but you won’t need the overhead. I haven’t found much documentation on the whole thing, but this thread might be a good starting point.¹

However, it is perfectly possible that your code does not actually have to be called from the main thread, or that there is another option to solve your problem without going through Java. That would, however, depend on the problem you’re trying to solve.


Note: I assume the main thread scenario. If you can use a native-code-based looper in the thread you want to deploy, you have more options.

¹ It is possible that the ALooper could be used in some sort of client-mode to do that, as well. Very unsure about that.

Leave a Reply

Your email address will not be published. Required fields are marked *