Commit ee8bd263 authored by Volker Krause's avatar Volker Krause
Browse files

Support JNI handle pass-through also for method return values

For this we return a wrapper type instead of a converted complex type that
can implicitly convert to either the actual object or a JNI handle. This
way we can skip the potential expensive conversion when passing a return
value directly to another JNI method.
parent fb4d7078
......@@ -86,7 +86,12 @@ private Q_SLOTS:
// implicit conversion from a static property wrapper
QCOMPARE(obj.handle().protocol().size(), 14);
// avoiding type conversion on return values
j = obj.getName();
// call chaining with JNI handle pass-through
QCOMPARE(obj.handle().protocol().size(), 17);
QCOMPARE(obj.handle().protocol()[0], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[1], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[2], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
......@@ -102,7 +107,9 @@ private Q_SLOTS:
QCOMPARE(obj.handle().protocol()[11], QLatin1String("callObjectMethod: getIntent ()Landroid/content/Intent; ()"));
QCOMPARE(obj.handle().protocol()[12], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
QCOMPARE(obj.handle().protocol()[13], QLatin1String("callMethod: setFlags (I)V (I)"));
QCOMPARE(obj.handle().protocol()[14], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[15], QLatin1String("callObjectMethod: getName ()Ljava/lang/String; ()"));
QCOMPARE(obj.handle().protocol()[16], QLatin1String("callMethod: setName (Ljava/lang/String;)V (o)"));
#if 0
// stuff that must not compile
......@@ -208,7 +208,7 @@ void ApplicationController::importFromIntent(const KAndroidExtras::Intent &inten
// opening a URL, can be something to import or a shortcut path
if (action == Intent::ACTION_VIEW) {
const auto url = intent.getData();
const QUrl url = intent.getData();
if (url.scheme() == QLatin1String("page")) {
qCDebug(Log) << url;
......@@ -220,7 +220,7 @@ void ApplicationController::importFromIntent(const KAndroidExtras::Intent &inten
// shared data, e.g. from email applications like FairMail
if (action == Intent::ACTION_SEND || action == Intent::ACTION_SEND_MULTIPLE) {
const auto type = intent.getType();
const QString type = intent.getType();
const auto subject = intent.getStringExtra(Intent::EXTRA_SUBJECT);
const auto from = intent.getStringArrayExtra(Intent::EXTRA_EMAIL);
const auto text = intent.getStringExtra(Intent::EXTRA_TEXT);
......@@ -77,6 +77,24 @@ namespace Internal {
template <typename T> inline constexpr T toFinalCallArgument(T value) { return value; }
inline jobject toFinalCallArgument(const QAndroidJniObject &value) { return value.object(); }
// method return value wrapper for complex types we can implicitly convert
// this defers conversion until it's actually needed, so we can do direct JNI handle pass-through
// when chaining calls
template <typename RetT>
class ReturnValue {
explicit inline ReturnValue(const QAndroidJniObject &v) : value(v) {}
inline operator QAndroidJniObject() const {
return value;
inline operator typename Jni::converter<RetT>::type() const {
return Jni::converter<RetT>::convert(value);
QAndroidJniObject value;
// return type conversion
template <typename RetT>
struct call_return {
......@@ -84,12 +102,12 @@ namespace Internal {
static inline constexpr bool is_convertible = !std::is_same_v<typename Jni::converter<RetT>::type, void>;
typedef std::conditional_t<is_basic, RetT, QAndroidJniObject> JniReturnT;
typedef std::conditional_t<is_basic || !is_convertible, JniReturnT, typename Jni::converter<RetT>::type> CallReturnT;
typedef std::conditional_t<is_basic || !is_convertible, JniReturnT, ReturnValue<RetT>> CallReturnT;
static inline constexpr CallReturnT toReturnValue(JniReturnT value)
if constexpr (is_convertible) {
return Jni::converter<RetT>::convert(value);
return ReturnValue<RetT>(value);
} else {
return value;
......@@ -169,9 +187,16 @@ namespace Internal {
* - basic types have to match exactly
* - non-basic types can be either passed as @c QAndroidJniObject instance or with a type that has an
* conversion registered with @c JNI_DECLARE_CONVERTER.
* The return type is handled similarly, if a conversion to a native type is available that is returned,
* @c QAndroidJniObject otherwise.
* Can only be placed in classes with a @c JNI_OBJECT.
* The return type of the method is determined as follows:
* - basic types are returned directly
* - non-basic types without a registered type conversion are returned as @c QAndroidJniObject.
* - non-basic types with a registered type conversion are returned in a wrapper class that can
* be implicitly converted either to the destination type of the conversion, or a @c QAndroidJniObject.
* This allows to avoid type conversion when chaining calls for example, it however needs additional
* care when used in combination with automatic type deduction.
* Thie macro can only be placed in classes with a @c JNI_OBJECT.
* @param RetT The return type. Must either be a basic type or a type declared with @c JNI_TYPE
* @param Name The name of the method. Must match the JNI method to be called exactly.
......@@ -180,7 +205,8 @@ namespace Internal {
#define JNI_METHOD(RetT, Name, ...) \
template <typename ...Args> \
inline Internal::call_return<RetT>::CallReturnT Name(Args&&... args) const { \
inline KAndroidExtras::Internal::call_return<RetT>::CallReturnT Name(Args&&... args) const { \
using namespace KAndroidExtras; \
return Internal::invoker<RetT, ## __VA_ARGS__>::call(handle(), "" #Name, Jni::signature<RetT(__VA_ARGS__)>(), std::forward<Args>(args)...); \
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment