Android JNI-来自C ++的Android UI线程上的调用函数 [英] Android JNI - Call function on Android UI thread from C++

查看:159
本文介绍了Android JNI-来自C ++的Android UI线程上的调用函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们的游戏引擎Cocos2d-x在自己的non-Java-UI-thread上本机运行在android上.我们需要通过Android UI thread上的JNIC++调用某些Java函数.

Our game engine Cocos2d-x runs natively on android on its own non-Java-UI-thread. We need to call certain Java functions from C++ via JNI on the Android UI thread.

要调用JNI-Functions,我们从此处(GitHub)使用JNIHelper.h/cpp: JniHelper.h JniHelper.cpp

For calling JNI-Functions, we're using the JNIHelper.h/cpp from here (GitHub): JniHelper.h, JniHelper.cpp

例如下面的C ++代码:

For example this C++ code:

auto retVal = JniHelper::callStaticStringMethod("org/utils/Facebook",
                         "getFacebookTokenString");

理想情况下,我们希望所有这些调用都在Android UI thread上发生,并传递std::function作为参数,一旦完成函数调用,将再次使用Cocos2d-x-thread的返回值进行调用.

Ideally, we'd like to have all these calls happen on the Android UI thread and pass an std::function as a parameter which is called with the return value on the Cocos2d-x-thread again once the function call is done.

理想的函数调用方式:

auto retVal = JniHelper::callStaticStringMethod("org/utils/Facebook",
  "getFacebookTokenString", [=](std::string retVal) {
 printf("This is the retval on the C++ caller thread again: %s", retVal.c_str());
});

但是也有许多没有返回值的调用,因此对于那些调用,只需在Java线程上调用它们会更容易.

But there are also many calls without any return value, so for those it should be easier to just call them on the java thread.

推荐答案

正如@Elviss所提到的-将代码发布到主线程中,您应该使用Looper.实际上,无需额外处理JNI和创建自定义java.lang.Runnable并通过复杂的JNI内容将其发布就可以完成此操作.

As @Elviss has mentioned - to post your code to main thread you should use Looper. Actually this may be done without extra coping with JNI and creating of custom java.lang.Runnable and posting it via complicated JNI stuff.

Android NDK提供了极其轻巧和有效的方式来将您的本机代码发布到任意循环程序.关键是您应该向循环程序提供任意文件描述符,并指定您感兴趣的文件事件(输入,输出等).在后台,looper会轮询该文件描述符,一旦事件可用,它将在适当的线程上运行您的回调.

Android NDK offers extremely lightweight and efficient way to post your native code to the arbitrary looper. The key point is that you should provide arbitrary file descriptor to the looper and specify what file events you are interested in (input, output, so on). Under the hood looper will poll that file descriptor and once event becomes available - it runs your callback on proper thread.

有一个最小的示例(没有错误检查和删除):

There is the minimal example (no error checks and teardowns):

#include <android/looper.h>
#include <unistd.h>

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "sergik", __VA_ARGS__)

static ALooper* mainThreadLooper;
static int messagePipe[2];

static int looperCallback(int fd, int events, void* data);

void someJniFuncThatYouShouldCallOnceOnMainThread() {
    mainThreadLooper = ALooper_forThread(); // get looper for this thread
    ALooper_acquire(mainThreadLooper); // add reference to keep object alive
    pipe(messagePipe); //create send-receive pipe
    // listen for pipe read end, if there is something to read
    // - notify via provided callback on main thread
    ALooper_addFd(mainThreadLooper, messagePipe[0],
                  0, ALOOPER_EVENT_INPUT, looperCallback, nullptr);
    LOGI("fd is registered");    

    // send few messages from arbitrary thread
    std::thread worker([]() {
        for(char msg = 100; msg < 110; msg++) {
            LOGI("send message #%d", msg);
            write(messagePipe[1], &msg, 1);
            sleep(1);
        }
    });
    worker.detach();
}

// this will be called on main thread
static int looperCallback(int fd, int events, void* data) {
    char msg;
    read(fd, &msg, 1); // read message from pipe
    LOGI("got message #%d", msg);
    return 1; // continue listening for events
}

此代码产生下一个输出:

This code produces next output:

06-28 23:28:27.076 30930-30930/? I/sergik: fd is registered
06-28 23:28:27.076 30930-30945/? I/sergik: send message #100
06-28 23:28:27.089 30930-30930/? I/sergik: got message #100
06-28 23:28:28.077 30930-30945/? I/sergik: send message #101
06-28 23:28:28.077 30930-30930/? I/sergik: got message #101
06-28 23:28:29.077 30930-30945/? I/sergik: send message #102
06-28 23:28:29.078 30930-30930/? I/sergik: got message #102
06-28 23:28:30.078 30930-30945/? I/sergik: send message #103
06-28 23:28:30.078 30930-30930/? I/sergik: got message #103
06-28 23:28:31.079 30930-30945/? I/sergik: send message #104
06-28 23:28:31.079 30930-30930/? I/sergik: got message #104
06-28 23:28:32.079 30930-30945/? I/sergik: send message #105
06-28 23:28:32.080 30930-30930/? I/sergik: got message #105
06-28 23:28:33.080 30930-30945/? I/sergik: send message #106
06-28 23:28:33.080 30930-30930/? I/sergik: got message #106
06-28 23:28:34.081 30930-30945/? I/sergik: send message #107
06-28 23:28:34.081 30930-30930/? I/sergik: got message #107
06-28 23:28:35.081 30930-30945/? I/sergik: send message #108
06-28 23:28:35.082 30930-30930/? I/sergik: got message #108
06-28 23:28:36.082 30930-30945/? I/sergik: send message #109
06-28 23:28:36.083 30930-30930/? I/sergik: got message #109

从pid-tid对中可以看到-消息是在主线程上接收的.当然,您可能会发送比一字节消息更复杂的内容.

As you see from pid-tid pairs - messages are received on main thread. And of course you may send something more complicated than one-byte messages.

这篇关于Android JNI-来自C ++的Android UI线程上的调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆