To run C++ code on Android UI (main) thread, you will have to use Android the looper (activity.getMainLooper() or Looper.getMainLooper() in Java):
jmethodID getMainLooperMethod = jniEnv->GetMethodID(mainActivityClass, "getMainLooper", "()Landroid/os/Looper;");
jobject mainLooper = jniEnv->CallObjectMethod(mainActivity, getMainLooperMethod);
"mainActivity" is an instance of android.app.Activity, that is passed to the JNI from Java, but you can also simply use the static getMainLooper method of the Looper class. Next you have to create an instance of Handler class (new Handler(mainLooper in Java):
jclass handlerClass = jniEnv->FindClass("android/os/Handler");
jmethodID handlerConstructor = jniEnv->GetMethodID(handlerClass, "<init>", "(Landroid/os/Looper;)V");
postMethod = jniEnv->GetMethodID(handlerClass, "post", "(Ljava/lang/Runnable;)Z");
handler = jniEnv->NewObject(handlerClass, handlerConstructor, mainLooper);
handler = jniEnv->NewGlobalRef(handler);
Be aware that you have to store the handler (jobject) to use it later.
You will have to write a bit of Java to implement the Runnable interface, so this code goes in Java:
package my.package;
import java.lang.Runnable;
public class Runner implements Runnable
{
native public void run();
}
As you can see the run() method is native, so we can implement it in C++ as follows:
extern "C" JNIEXPORT void JNICALL
Java_my_package_Runner_run(JNIEnv*, jclass)
{
// here goes your native code
}
Now you have to get the Runner class and its constructor in C++:
runnerClass = jniEnv->FindClass("org/ouzelengine/Runner");
runnerClass = static_cast<jclass>(jniEnv->NewGlobalRef(runnerClass));
runnerConstructor = jniEnv->GetMethodID(runnerClass, "<init>", "()V");
Store the runnerClass (jclass) and runnerConstructor (jmethodID) somewhere for later use. The final thing you have to do is actually create the instance of the Runner class and post it to the handler:
jobject runner = jniEnv->NewObject(runnerClass, runnerConstructor);
if (!jniEnv->CallBooleanMethod(handler, postMethod, runner))
{
// something wrong happened
}
What I do in the Ouzel engines code is I create a queue of std::function's and guard it with a mutex. Whenever I need to execute a std::function on Android UI thread, I add the std::function instance to the queue and pop it from the queue and execute it in the native method (Java_my_package_Runner_run).
This is the closest you can get to writing no Java code (you will have to write 6 lines of it to implement the Runnable interface).