Better_Software_Header_MobileBetter_Software_Header_Web

Find what you need - explore our website and developer resources

Qt on Android: How to run C++ code on Android UI thread

Useful features you need on Android that don't have a Qt API


namespace KDAB {
	namespace Android {
		typedef std::function<void()> Runnable;

		/// Posts a runnable on Android thread, then exists.
		/// If you call runOnAndroidThread from Android UI thread,
		/// it will execute the runnable immediately
		void runOnAndroidThread(const Runnable &runnable);

		/// Posts a runnable on Android thread then waits until it's executed.
		void runOnAndroidThreadSync(const Runnable &runnable, int waitMs = INT_MAX);
	} // namespace Android
} // KDAB

namespace KDAB {
	namespace Android {

		static std::deque<Runnable> s_pendingRunnables;
		static std::mutex s_pendingRunnablesMutex;

		void runOnAndroidThread(const Runnable &runnable)
		{
			s_pendingRunnablesMutex.lock();
			bool triggerRun = s_pendingRunnables.empty();
			s_pendingRunnables.push_back(runnable);
			s_pendingRunnablesMutex.unlock();
			if (triggerRun) {
				QtAndroid::androidActivity().callMethod<void>("runOnUiThread",
				"(Ljava/lang/Runnable;)V",
				QAndroidJniObject("com/kdab/android/utils/Runnable").object());
			}
		}

		void runOnAndroidThreadSync(const Runnable &runnable, int waitMs)
		{
			std::shared_ptr<QSemaphore> sem = std::make_shared<QSemaphore>();
			runOnAndroidThread([sem, &runnable](){
				runnable();
				sem->release();
			});
			sem->tryAcquire(1, waitMs);
		}

		extern "C" JNIEXPORT void JNICALL Java_com_kdab_android_utils_Runnable_runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
		{
			for (;;) { // run all posted runnables
				s_pendingRunnablesMutex.lock();
				if (s_pendingRunnables.empty()) {
					s_pendingRunnablesMutex.unlock();
					break;
				}
				Runnable runnable(std::move(s_pendingRunnables.front()));
				s_pendingRunnables.pop_front();
				s_pendingRunnablesMutex.unlock();
				runnable();
			}
		}

	} // namespace Android
} // KDAB

package com.kdab.android.utils;

class Runnable implements java.lang.Runnable
{
	@Override
	public void run() {
		runPendingCppRunnables();
	}

	public static native void runPendingCppRunnables();
}

QAndroidJniObject m_button; // declared in your .h file

// Implementation
Button::Button
{
	KDAB::Android::runOnAndroidThreadSync([this]{
		m_button = QAndroidJniObject("android/widget/Button",
		"(Landroid/content/Context;)V",
		QtAndroid::androidActivity().object());
	});
}

void Button::setText(const QString &text)
{
	KDAB::Android::runOnAndroidThread([text, this]{
		m_button.callMethod<void>("setText", "(Ljava/lang/CharSequence;)V",
		QAndroidJniObject::fromString(text).object());
	});
}

QString Button::text() const
{
	QString res;
	KDAB::Android::runOnAndroidThreadSync([&res, this]{
		res = m_button.callObjectMethod("getText", "()Ljava/lang/CharSequence;").toString();
	});
	return res;
}

2 Comments

7 - Apr - 2017

Jason

10 - Oct - 2019

Jan