qjni, how to pass a qstring?
-
I'm not sure what I'm doing wrong as the only response is an immediate crash, but it happens in the callStaticMethod line:
QString path = uri.toLocalFile(); QJniObject java_string = QJniObject::fromString(path); auto java_path = QJniObject::callStaticMethod<jclass>("java/nio/file/Paths", "get", "(Ljava/lang/String;)Ljava/nio/file/Path;", java_string);
The
uri
is a 'content://
' one that Android gives me back from the file dialog, and I want to figure out the mimetype of the thing.The Java code I'm trying to call is;
java.nio.file.Path path = java.nio.file.Paths.get(myPath); String mime = java.nio.file.Files.probeContentType(path);
If I replace that
java_string
with something like "foo", it doesn't crash. So how am I supposed to pass in an argument of type java.lang.String if not like above? -
ok, I figured it out. The problems are in big part due to the very bad documentation. The Java world is known for its really crappy documentation level and using JNI without documenting this properly means we just inherit the less-than-pretty state of affairs.
Add on top of that the fact that the JNI stuff in Qt is designed for the ideal "it just works" case and if you deviate you're in the bushes looking for your lenses.
For instance in JNI calling a method with more than one argument does NOT work if you put a comma between argument-types.
In Qt doing so anyway will just silently fail. Like the method returned null instead of the method not being found at all...Then the docs in Qt sometimes refer to things like jobject, sometimes to jclass and they are both interchangably used to mean QJniObject. Not to mention jstring and jint...
Not sure what the intention there is, but it failed quite spectacularly. Again, the QJniObject could be re-done to be much better. Dropping the template magic would likely go a long way in that direction.So this is quite painful and I hope Qt will re-do the JNI stuff to become magnitudes better than it is, it has that potential. This was a painful 2 days.
I ended up actually adding a Java file for the, honestly embarrassingly small amount of code due to things like me not figuring out how to make a string-array that I can pass into a Java method when I needed to use the
ContentResolver.query()
method to find the filename.The Java Code:
public class Utils { public static String getMimeType(Context context, Uri uri) { ContentResolver cr = context.getContentResolver(); return mimeType = cr.getType(uri); } }
The C++ code
QJniEnvironment env; auto myUtils = env.findClass("tomsapp/Utils"); auto context = QJniObject(QNativeInterface::QAndroidApplication::context()); QJniObject java_string = QJniObject::fromString(m_addedFiles.at(index)); auto uri = QJniObject::callStaticMethod<jclass>("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", java_string.object<jstring>()); auto mime = QJniObject::callStaticMethod<jclass>(myUtils, "getMimeType", "(Landroid/content/Context;Landroid/net/Uri;)Ljava/lang/String;", context.object(), uri.object()); QString mimeType = mime.toString();
So, in the end I probably didn't figure out the actual answer of what I did wrong. Likely some character at a wrong location that caused the lookup to not work, if someone figures it out in the above posts, it might be fun to learn. Otherwise :shrug: this work. Avoid QJniObject as much as you can, it's fragile as F.
-
My latest version is this,
I still only get empty string as answer:auto context = QJniObject(QNativeInterface::QAndroidApplication::context()); auto contentResolver = context.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;"); QJniObject java_string = QJniObject::fromString(m_addedFiles.at(index)); auto uri = QJniObject::callStaticMethod<jclass>("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", java_string.object<jstring>()); auto mimeType_java = contentResolver.callObjectMethod("getType", "(Landroid/net/Uri;)Ljava.util.String;", uri.object()); QString mimeType = mimeType_java.toString();
Well, the
mimeType_java.isValid()
returns false, so maybe it's a null answer, which is what the API is documented to give if the URI is invalid or unknown. -
ok, I figured it out. The problems are in big part due to the very bad documentation. The Java world is known for its really crappy documentation level and using JNI without documenting this properly means we just inherit the less-than-pretty state of affairs.
Add on top of that the fact that the JNI stuff in Qt is designed for the ideal "it just works" case and if you deviate you're in the bushes looking for your lenses.
For instance in JNI calling a method with more than one argument does NOT work if you put a comma between argument-types.
In Qt doing so anyway will just silently fail. Like the method returned null instead of the method not being found at all...Then the docs in Qt sometimes refer to things like jobject, sometimes to jclass and they are both interchangably used to mean QJniObject. Not to mention jstring and jint...
Not sure what the intention there is, but it failed quite spectacularly. Again, the QJniObject could be re-done to be much better. Dropping the template magic would likely go a long way in that direction.So this is quite painful and I hope Qt will re-do the JNI stuff to become magnitudes better than it is, it has that potential. This was a painful 2 days.
I ended up actually adding a Java file for the, honestly embarrassingly small amount of code due to things like me not figuring out how to make a string-array that I can pass into a Java method when I needed to use the
ContentResolver.query()
method to find the filename.The Java Code:
public class Utils { public static String getMimeType(Context context, Uri uri) { ContentResolver cr = context.getContentResolver(); return mimeType = cr.getType(uri); } }
The C++ code
QJniEnvironment env; auto myUtils = env.findClass("tomsapp/Utils"); auto context = QJniObject(QNativeInterface::QAndroidApplication::context()); QJniObject java_string = QJniObject::fromString(m_addedFiles.at(index)); auto uri = QJniObject::callStaticMethod<jclass>("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", java_string.object<jstring>()); auto mime = QJniObject::callStaticMethod<jclass>(myUtils, "getMimeType", "(Landroid/content/Context;Landroid/net/Uri;)Ljava/lang/String;", context.object(), uri.object()); QString mimeType = mime.toString();
So, in the end I probably didn't figure out the actual answer of what I did wrong. Likely some character at a wrong location that caused the lookup to not work, if someone figures it out in the above posts, it might be fun to learn. Otherwise :shrug: this work. Avoid QJniObject as much as you can, it's fragile as F.
-