Android: print jni exceptions from Qt instead of ExceptionDescribe()
This makes the exceptions prints tagged with the app's name/tag, and also can allow QTest::ignoreMessage() to handle exceptions as now it cannot filter messages out because they're printed by the Android system. Change-Id: I9f5132b9ec5b5cd8fb35707eaaf68aa517f417ec Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> (cherry picked from commit dbb622a38d6ce4fbd7fba010aea238e5f9552c67) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
7216a936bc
commit
47dfdc2c61
@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
|
||||||
#include "qjnienvironment.h"
|
#include "qjnienvironment.h"
|
||||||
#include "qjniobject.h"
|
|
||||||
#include "qjnihelpers_p.h"
|
#include "qjnihelpers_p.h"
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
@ -444,17 +443,58 @@ bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod
|
|||||||
*/
|
*/
|
||||||
bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
|
bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
|
||||||
{
|
{
|
||||||
if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
|
return checkAndClearExceptions(d->jniEnv, outputMode);
|
||||||
if (outputMode != OutputMode::Silent)
|
|
||||||
d->jniEnv->ExceptionDescribe();
|
|
||||||
d->jniEnv->ExceptionClear();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Any pending exception need to be cleared before calling this
|
||||||
|
QString exceptionMessage(JNIEnv *env, const jthrowable &exception)
|
||||||
|
{
|
||||||
|
if (!exception)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto logError = []() {
|
||||||
|
qWarning() << "QJniEnvironment: a null object returned or an exception occurred while "
|
||||||
|
"fetching a prior exception message";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkAndClear = [env]() {
|
||||||
|
if (Q_UNLIKELY(env->ExceptionCheck())) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const jclass logClazz = env->FindClass("android/util/Log");
|
||||||
|
if (checkAndClear() || !logClazz) {
|
||||||
|
logError();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const jmethodID methodId = env->GetStaticMethodID(logClazz, "getStackTraceString",
|
||||||
|
"(Ljava/lang/Throwable;)Ljava/lang/String;");
|
||||||
|
if (checkAndClear() || !methodId) {
|
||||||
|
logError();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
jvalue value;
|
||||||
|
value.l = static_cast<jobject>(exception);
|
||||||
|
const jobject messageObj = env->CallStaticObjectMethodA(logClazz, methodId, &value);
|
||||||
|
const jstring jmessage = static_cast<jstring>(messageObj);
|
||||||
|
if (checkAndClear())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
char const *utfMessage = env->GetStringUTFChars(jmessage, 0);
|
||||||
|
const QString message = QString::fromUtf8(utfMessage);
|
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(jmessage, utfMessage);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
} // end namespace
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
|
\fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
|
||||||
|
|
||||||
@ -472,9 +512,22 @@ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode output
|
|||||||
bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
|
bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
|
||||||
{
|
{
|
||||||
if (Q_UNLIKELY(env->ExceptionCheck())) {
|
if (Q_UNLIKELY(env->ExceptionCheck())) {
|
||||||
if (outputMode != OutputMode::Silent)
|
if (outputMode == OutputMode::Verbose) {
|
||||||
env->ExceptionDescribe();
|
if (jthrowable exception = env->ExceptionOccurred()) {
|
||||||
env->ExceptionClear();
|
env->ExceptionClear();
|
||||||
|
const QString message = exceptionMessage(env, exception);
|
||||||
|
// Print to QWARN since env->ExceptionDescribe() does the same
|
||||||
|
if (!message.isEmpty())
|
||||||
|
qWarning().noquote() << message;
|
||||||
|
env->DeleteLocalRef(exception);
|
||||||
|
} else {
|
||||||
|
// if the exception object is null for some reason just
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user