Android JNI - Call function on Android UI thread from C++

后端 未结 4 1731
醉梦人生
醉梦人生 2020-12-08 17:51

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

4条回答
  •  醉话见心
    2020-12-08 18:17

    Based on @Sergio's answer, here is a simple wrapper NativeHandler which can take function, function object and lambda as argument, try to mimic the behavior of android.os.Handler

    class NativeHandler {
    public:
        static constexpr auto TAG = "NativeHandler";
        static NativeHandler* forCurrentThread() {
            return new NativeHandler;
        }
    
        template
        bool post(FUNC&& func, ARGS&&... args) {
            auto callable = new Callable(func, std::forward(args)...);
            write(_pipeFDS[1], &callable, sizeof(decltype(callable)));
            return true;
        }
    
        NativeHandler(const NativeHandler&) = delete;
        NativeHandler(NativeHandler&&) = delete;
        NativeHandler& operator=(const NativeHandler&) = delete;
        NativeHandler& operator=(NativeHandler&&) = delete;
        virtual ~NativeHandler() {
            ALooper_removeFd(_looper, _pipeFDS[0]);
            ALooper_release(_looper);
            close(_pipeFDS[0]);
            close(_pipeFDS[1]);
        }
    
    private:
        class Callable {
        public:
            void call() {
                if (_function) _function();
            }
    
            template
            Callable(FUNC func, ARGS... args) : _function(std::bind(func, args...)) {}
    
            Callable() = delete;
            Callable(const Callable&) = delete;
            Callable(Callable&&) = delete;
            Callable operator=(const Callable&) = delete;
            Callable operator=(Callable&&) = delete;
            virtual ~Callable() {}
        private:
            std::function _function;
        };
    
        NativeHandler() {
            if (pipe(_pipeFDS) != 0) {
                throw std::bad_alloc();
            }
            _looper = ALooper_forThread();
            ALooper_acquire(_looper);
            if (ALooper_addFd(_looper, _pipeFDS[0], ALOOPER_POLL_CALLBACK,
                              ALOOPER_EVENT_INPUT, _looperCallback, nullptr) == -1) {
                throw std::bad_alloc();
            }
        };
    
        ALooper* _looper;
        int _pipeFDS[2];
        static int _looperCallback(int fd, int events, void* data) {
            void* buf = new char[sizeof(Callable*)];
            ssize_t nr = read(fd, buf, sizeof(Callable*));
            Callable* callable = *((Callable**)buf);
            __android_log_print(ANDROID_LOG_INFO, "Callable", "read size is %d %p", nr, callable);
            callable->call();
            delete[] buf;
            return 1;
        }
    };
    

    And then the use example, hope it might be helpful for anyone who want similar behavior with android java api handler in JNI.

    void f(char c, short s) {
        __android_log_print(ANDROID_LOG_DEBUG, NativeHandler::TAG, "%s c = %c, s = %d", __FUNCTION__, c, s);
    }
    
    struct Task {
        void operator()(int i, double d) {
            __android_log_print(ANDROID_LOG_DEBUG, NativeHandler::TAG, "Task i = %d, d = %f", i, d);
        }
    };
    
    // ...
    auto handler = NativeHandler::forCurrentThread();
    std::thread worker([handler]() {
        handler->post([](int i, double d, void* p) {
            __android_log_print(ANDROID_LOG_DEBUG, "NativeHandler", "i = %d, d = %f, p = %p", i, d, p);
        }, 100, -123.4, nullptr);
    
        handler->post(f, 'c', 128);
        handler->post(Task(), 123, 3.1415926);
    });
    worker.detach();
    

提交回复
热议问题