Android: Improve cache logic in findClass()

This change adds guards to ensure that we only do a class
look-up once when calling QJNIEnvironmentPrivate::findClass().
IF someone calls findClass() with an environment that does not contain
a "valid" class loader, we should fallback to loadClass(),
but only once.

Change-Id: If5fc82956db889f3269bb33c98a16c49cae55def
Reviewed-by: Yoann Lopes <yoann.lopes@theqtcompany.com>
This commit is contained in:
Christian Strømme 2014-09-29 14:16:28 +02:00 committed by Christian Stromme
parent cf8f369f85
commit 3d94a564f4

View File

@ -80,38 +80,22 @@ static QString toDotEncodedClassName(const char *className)
return QString::fromLatin1(className).replace(QLatin1Char('/'), QLatin1Char('.'));
}
static jclass getCachedClass(const QString &classDotEnc)
static jclass getCachedClass(const QString &classDotEnc, bool *isCached = 0)
{
QHash<QString, jclass>::iterator it = cachedClasses->find(classDotEnc);
const bool found = (it != cachedClasses->end());
if (it == cachedClasses->end())
return 0;
if (isCached != 0)
*isCached = found;
return it.value();
return found ? it.value() : 0;
}
static jclass findClass(const char *className, JNIEnv *env)
static jclass loadClassDotEnc(const QString &classDotEnc, JNIEnv *env)
{
const QString &classDotEnc = toDotEncodedClassName(className);
jclass clazz = getCachedClass(classDotEnc);
if (clazz != 0)
return clazz;
jclass fclazz = env->FindClass(className);
if (!exceptionCheckAndClear(env)) {
clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
env->DeleteLocalRef(fclazz);
}
cachedClasses->insert(classDotEnc, clazz);
return clazz;
}
static jclass loadClass(const char *className, JNIEnv *env)
{
const QString &classDotEnc = toDotEncodedClassName(className);
jclass clazz = getCachedClass(classDotEnc);
if (clazz != 0)
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
if (clazz != 0 || isCached)
return clazz;
QJNIObjectPrivate classLoader = QtAndroidPrivate::classLoader();
@ -130,6 +114,11 @@ static jclass loadClass(const char *className, JNIEnv *env)
return clazz;
}
inline static jclass loadClass(const char *className, JNIEnv *env)
{
return loadClassDotEnc(toDotEncodedClassName(className), env);
}
typedef QHash<QString, jmethodID> JMethodIDHash;
Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
@ -224,12 +213,28 @@ JNIEnv *QJNIEnvironmentPrivate::operator->()
jclass QJNIEnvironmentPrivate::findClass(const char *className, JNIEnv *env)
{
jclass clazz = 0;
if (env != 0)
clazz = ::findClass(className, env);
const QString &classDotEnc = toDotEncodedClassName(className);
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
if (clazz == 0)
clazz = loadClass(className, QJNIEnvironmentPrivate());
const bool found = (clazz != 0) || (clazz == 0 && isCached);
if (found)
return clazz;
if (env != 0) { // We got an env. pointer (We expect this to be the right env. and call FindClass())
jclass fclazz = env->FindClass(className);
if (!exceptionCheckAndClear(env)) {
clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
env->DeleteLocalRef(fclazz);
}
if (clazz != 0)
cachedClasses->insert(classDotEnc, clazz);
}
if (clazz == 0) // We didn't get an env. pointer or we got one with the WRONG class loader...
clazz = loadClassDotEnc(classDotEnc, QJNIEnvironmentPrivate());
return clazz;
}