Подтвердить что ты не робот

Как отправить код для запуска в основном потоке Android из отдельного потока в С++?

У меня есть отдельный поток, работающий на С++ в фоновом режиме, и я хочу, чтобы он мог отправлять код для запуска в другом потоке, который уже запускает android.os.Looper(например, основной поток). Под "post" я имею в виду нечто похожее на View#post, где Runnable помещается в очередь для запуска в цикле событий. Код, который будет выполнен, также написан на С++.

Я нашел API ALooper (http://developer.android.com/ndk/reference/group___looper.html), но документы невелики, и мне неясно, получает ли ALooper связанный с потоком адресата, добавление другого FD и сигнализация, это заставит мой код поддерживать правильный порядок в очереди событий по отношению к другим включенным Runnables.

Я бы предпочел не проходить через Java и получать Handler и т.д. - это просто кажется ненужным, поскольку и код, который я пытаюсь запустить, и код, который его отправляет, находятся в С++.

4b9b3361

Ответ 1

В потоке может быть только один связанный с ним 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-сообщения и фреймворк concurrency для разработки собственного кода, описанный в отличном сообщение в блоге.

Update

Вот требуемое доказательство. Ниже приведена стекная трассировка при работе над несвязанной проблемой:

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)

Ответ 2

Вам нужна функция, уже выполняемая в основном потоке. Если вы вызываете ALooper_forThread() или ALooper_prepare(), вы получите указатель на петлитель, связанный с основным потоком. Не забудьте вызвать ALooper_acquire(), чтобы он мог использоваться для разных потоков.

Ответ 3

Это может помочь вам https://groups.google.com/forum/#!topic/android-ndk/v2OITtaZTes

Но это легко достичь с помощью обработчика со стороны java, отправки и обработки сообщений, выполняющих обратную связь между native и java с помощью jni-вызовов.

Ответ 4

Вам нужно будет пройти через Java, поскольку android.os.Looper не реализован в собственном коде (по крайней мере, в в настоящее время больше всего недавний коммит).

У меня недостаточно опыта работы с NDK, чтобы быстро ввести требуемый шаблон, но очевидным вариантом является создание java Runnable на основе собственного кода и отправка его в петлеукладчик.

Не столь очевидное решение непосредственно работает на MessageQueue потока. Если у вас есть ссылка на это, вы можете зарегистрировать один конец родного канала и записать сообщения на другой конец; труба в основном принимает функцию Handler, но на собственный код. Технически, ваш код по-прежнему вызывается из Java, но вам не нужны накладные расходы. Я не нашел много документации по всему, но этот поток мог бы стать хорошей отправной точкой.¹

Однако вполне возможно, что ваш код на самом деле не должен вызываться из основного потока или что есть другой вариант для решения вашей проблемы, не проходя через Java. Это, однако, будет зависеть от проблемы, которую вы пытаетесь решить.


Примечание. Я предполагаю сценарий основного потока. Если в потоке, который вы хотите развернуть, вы можете использовать петлитель на основе исходного кода, у вас есть больше параметров.

¹ Возможно, что ALooper можно использовать в каком-то режиме клиента, чтобы это сделать. Очень не уверен в этом.

Ответ 5

Если вы хотите сделать некоторые материалы в основном потоке из другого потока, я предлагаю вам использовать функцию 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.

Надеюсь, это поможет.