Android: Fix QtAbstractItemModel proxy threading problem

This change sets the thread affinity of the
QAndroidItemModelProxy instance to the Qt main thread,
enabling its APIs to be called from both the Qt main thread
and Android threads. Synchronization between threads is
ensured by using the newly introduced
QAndroidItemModelProxy::safeCall(), which utilizes
QMetaObject::invokeMethod() with the appropriate
connection type based on the calling thread.

For void functions, Qt::AutoConnection can be used.
Functions returning values will either use a
Qt::DirectConnection if invoked from the same thread as
the QAndroidItemModelProxy instance, or a
Qt::BlockingQueuedConnection if called from a different
thread.

Fixes: QTBUG-127701
Change-Id: I214bc43d20d8bdf301fc97920493415d503d26d8
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit 6c30a60a42800dd5d732d2c7018d87e8f3a9c6a0)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 1f0cf1e6e5625587caec3951c9a439ac0b3e3cfb)
This commit is contained in:
Soheil Armin 2024-08-07 14:06:12 +03:00 committed by Qt Cherry-pick Bot
parent 72c6fcafdd
commit 505ee95fda
2 changed files with 42 additions and 9 deletions

View File

@ -5,6 +5,8 @@
#include <QtCore/private/qandroidmodelindexproxy_p.h>
#include <QtCore/private/qandroidtypeconverter_p.h>
#include <QtCore/qcoreapplication.h>
QT_BEGIN_NAMESPACE
using namespace QtJniTypes;
@ -131,7 +133,12 @@ QAndroidItemModelProxy::createNativeProxy(QJniObject itemModel)
{
QAbstractItemModel *nativeProxy = nativeInstance(itemModel);
if (!nativeProxy) {
Q_ASSERT(QCoreApplication::instance());
nativeProxy = new QAndroidItemModelProxy(itemModel);
QThread *qtMainThread = QCoreApplication::instance()->thread();
if (nativeProxy->thread() != qtMainThread)
nativeProxy->moveToThread(qtMainThread);
itemModel.callMethod<void>("setNativeReference", reinterpret_cast<jlong>(nativeProxy));
connect(nativeProxy, &QAndroidItemModelProxy::destroyed, nativeProxy, [](QObject *obj) {
@ -205,7 +212,8 @@ jobject QAndroidItemModelProxy::jni_createIndex(JNIEnv *env, jobject object, jin
{
QModelIndex (QAndroidItemModelProxy::*createIndexPtr)(int, int, quintptr) const =
&QAndroidItemModelProxy::createIndex;
const QModelIndex index = invokeNativeProxyMethod(env, object, createIndexPtr, row, column, id);
const QModelIndex index =
invokeNativeProxyMethod(env, object, createIndexPtr, row, column, quintptr(id));
return env->NewLocalRef(QAndroidModelIndexProxy::jInstance(index).object());
}

View File

@ -21,6 +21,7 @@
#include <QtCore/qjniobject.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjnitypes.h>
#include <QtCore/qthread.h>
QT_BEGIN_NAMESPACE
@ -58,34 +59,58 @@ public:
static QJniObject createProxy(QAbstractItemModel *abstractClass);
template <typename Func, typename... Args>
static auto invokeNativeProxyMethod(JNIEnv */*env*/, jobject jvmObject, Func func, Args &&...args)
static auto invokeNativeProxyMethod(JNIEnv * /*env*/, jobject jvmObject, Func &&func,
Args &&...args)
{
Q_ASSERT(jvmObject);
auto model = qobject_cast<QAndroidItemModelProxy *>(nativeInstance(jvmObject));
Q_ASSERT(model);
return std::invoke(func, model, std::forward<Args>(args)...);
return safeCall(model, std::forward<Func>(func), std::forward<Args>(args)...);
}
template <typename Func, typename... Args>
static auto invokeNativeMethod(JNIEnv */*env*/, jobject jvmObject, Func func, Args &&...args)
static auto invokeNativeMethod(JNIEnv * /*env*/, jobject jvmObject, Func &&func, Args &&...args)
{
Q_ASSERT(jvmObject);
auto model = nativeInstance(jvmObject);
Q_ASSERT(model);
return std::invoke(func, model, std::forward<Args>(args)...);
return safeCall(model, std::forward<Func>(func), std::forward<Args>(args)...);
}
template <typename Func1, typename Func2, typename... Args>
static auto invokeNativeImpl(JNIEnv */*env*/, jobject jvmObject, Func1 defaultFunc, Func2 func,
Args &&...args)
static auto invokeNativeImpl(JNIEnv * /*env*/, jobject jvmObject, Func1 &&defaultFunc,
Func2 &&func, Args &&...args)
{
Q_ASSERT(jvmObject);
auto nativeModel = nativeInstance(jvmObject);
auto nativeProxyModel = qobject_cast<QAndroidItemModelProxy *>(nativeModel);
if (nativeProxyModel)
return std::invoke(defaultFunc, nativeProxyModel, std::forward<Args>(args)...);
return safeCall(nativeProxyModel, std::forward<Func1>(defaultFunc),
std::forward<Args>(args)...);
else
return std::invoke(func, nativeModel, std::forward<Args>(args)...);
return safeCall(nativeModel, std::forward<Func2>(func), std::forward<Args>(args)...);
}
template <typename Object, typename Func, typename... Args>
static auto safeCall(Object *object, Func &&func, Args &&...args)
{
using ReturnType = decltype(std::invoke(std::forward<Func>(func), object,
std::forward<Args>(args)...));
if constexpr (std::is_void_v<ReturnType>) {
QMetaObject::invokeMethod(object, std::forward<Func>(func), Qt::AutoConnection,
std::forward<Args>(args)...);
} else {
ReturnType returnValue;
const auto connectionType = object->thread() == QThread::currentThread()
? Qt::DirectConnection
: Qt::BlockingQueuedConnection;
QMetaObject::invokeMethod(object, std::forward<Func>(func), connectionType,
qReturnArg(returnValue), std::forward<Args>(args)...);
return returnValue;
}
}
static jint jni_columnCount(JNIEnv *env, jobject object, JQtModelIndex parent);