Sur cette page

QtJenny : génération de classes proxy C++ pour accéder aux API Android

Démonstration présentant l'utilisation de QtJenny.

Application de démonstration QtJenny avec commandes de volume, de luminosité et de verrouillage de veille

Présentation générale

Cette démo présente l'utilisation de QtJenny et s'en sert pour générer des classes proxy C++ permettant d'accéder aux API Android à partir de code C++. Les classes C++ générées sont utilisées dans l'interface utilisateur de cette démo pour effectuer des actions telles que le réglage du volume et de la luminosité, l'activation et la désactivation des verrous de réveil, l'envoi de notifications et le déclenchement de vibrations. Ces actions font partie des API Android que Qt n'implémente pas.

La génération de classes C++ à l'aide de QtJenny évite d'avoir à écrire manuellement du code JNI.

Fonctionnement

La démo comporte deux parties distinctes : d'une part, l'application d'interface utilisateur, c'est-à-dire le projet « Qt Quick » nommé « qtjenny_consumer », qui utilise le code généré par QtJenny ; d'autre part, un ensemble de trois projets Gradle différents, nommés respectivement « qtjenny_general », « qtjenny_callback » et « qtjenny_baseclass », qui gèrent ensemble la génération de code à l'aide de QtJenny. Chacun de ces trois projets Gradle contient des annotations de classes et des configurations d’ Gradle s pour la génération de code par QtJenny.

La génération de code QtJenny est déclenchée automatiquement lors de la configuration CMake du projet qtjenny_consumer, par l’exécution de la tâche Gradle « kaptReleaseKotlin » dans chacun des projets Gradle qtjenny_general, qtjenny_callback et qtjenny_baseclass.

if (ANDROID)
    if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
        set (gradlew_cmd "gradlew.bat")
    else()
         set (gradlew_cmd "./gradlew")
    endif()
    set (gradlew_arg "--rerun-tasks")
    set (gradlew_task "kaptReleaseKotlin")
    execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_baseclass")
    execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_general")
    execute_process(COMMAND ${gradlew_cmd} ${gradlew_arg} ${gradlew_task}
    WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/qtjenny_callback")
else()
    message(FATAL_ERROR "Example only works on Android")
endif()

L'exécution du projet qtjenny_consumer lance l'application UI.

Génération des en-têtes C++

L'exécution de la tâche « kaptReleaseKotlin » dans les projets Gradle déclenche un processeur d'annotations qui traite les annotations déclarées dans ` GenerateCppCode.kt`.

Extrait de code tiré du fichier « GenerateCppCode.kt » de qtjenny_general:

@NativeClass
@NativeProxy(allMethods = false, allFields = false)
@NativeProxyForClasses(namespace = "android::os", classes = [BatteryManager::class, VibratorManager::class,
    Vibrator::class, VibrationEffect::class, Context::class, PowerManager::class, PowerManager.WakeLock::class,
    Handler::class, Looper::class])
@NativeProxyForClasses(namespace = "android::view", classes = [Window::class, WindowManager.LayoutParams::class])
@NativeProxyForClasses(namespace = "android::media", classes = [AudioManager::class])
@NativeProxyForClasses(namespace = "android::drawable", classes = [android.R.drawable::class])
@NativeProxyForClasses(namespace = "android::app", classes = [Activity::class, Notification::class,
    Notification.Builder::class, NotificationChannel::class, NotificationManager::class])
@NativeProxyForClasses(namespace = "android::provider", classes = [Settings.Global::class, Settings.System::class,
    Settings::class])
@NativeProxyForClasses(namespace = "android::content", classes = [Intent::class, ContentResolver::class])

Dans le cas de qtjenny_general, le processeur d’annotations génère les en-têtes C++ dans le répertoire qtjenny_output. Ces en-têtes C++ contiennent le code JNI standard nécessaire pour accéder aux API Android utilisées dans qtjenny_consumer.

Le script « app level » build.gradle situé dans qtjenny_general spécifie les arguments de la commande kapt implémentée par QtJenny. Ces arguments sont analysés par le compilateur QtJenny et utilisés lors du processus de génération.

kapt {
    arguments {
        // pass arguments to jenny
        arg("jenny.outputDirectory", project.file("../../qtjenny_output"))
        arg("jenny.templateDirectory", project.file("../templates"))
        arg("jenny.headerOnlyProxy", "true")
        arg("jenny.useJniHelper", "false")
        arg("jenny.useTemplates", "true")
    }
}
qtjenny_general

L'qtjenny_general se charge de générer des wrappers pour les classes Java, afin de permettre la construction d'objets Java et l'appel de méthodes sur ceux-ci. Ces wrappers encapsulent les appels de méthodes habituels, et pour les callbacks, les classes `qtjenny_baseclass` et `qtjenny_callback` sont utilisées.

qtjenny_baseclass

L'qtjenny_baseclass permet en effet d'étendre la classe Java ContentObserver dans du code C++, en combinant la génération de classes de base et la génération de callbacks.

qtjenny_callback

L'qtjenny_callback permet en effet d'implémenter une interface Java en C++. La classe qtjenny_baseclass, qui en est une extension, utilise également ce mécanisme.

Pour implémenter une interface, une classe proxy Java est créée à l'aide d'un InvocationHandler qui effectue des appels de fonctions virtuelles dans la hiérarchie de classes C++ générée.

Pour étendre une classe de base, la classe étendue générée effectue des appels de fonctions virtuelles au sein de la hiérarchie de classes C++ générée.

Utilisation des en-têtes C++ générés dans l'application « Qt Quick »

L'qtjenny_consumer est l'application « Qt Quick » qui utilise les en-têtes C++ générés par qtjenny_general. Nous incluons les en-têtes générés dans le fichier backend.h et les utilisons dans backend.cpp pour accéder à diverses API Android.

L'interface utilisateur de l'application se compose d'un fichier Main.qml, qui est relié à la classe BackEnd à l'aide des macros Q_INVOKABLE et Q_PROPERTY.

L'interface utilisateur contient les contrôles et actions suivants.

Verrous de réveil

Vous pouvez activer et désactiver des verrous de réveil complets ou partiels à l'aide de ` Switches`. Ces commutateurs déclenchent les fonctions ` Q_INVOKABLE ` correspondantes de la classe ` Backend `, qui activent le verrou de réveil et définissent le texte d'état du verrou de réveil qui s'affichera à l'utilisateur dans l'interface utilisateur.

if (checked) {
    myBackEnd.setFullWakeLock()
    if (partialWakeLock.checked)
        partialWakeLock.click()
    mainWindow.wakeLockStatus = "Full WakeLock active"

La configuration d’un verrouillage de réveil partiel s’effectue via la classe ` WakeLockProxy `, qui s’appuie sur l’API Android ` PowerManager.WakeLock `. La configuration d’un verrouillage de réveil complet s’effectue via la classe ` WindowProxy `, qui s’appuie sur l’API Android ` Window `.

Vibration

Vous pouvez déclencher une vibration à l’aide de la fonction BackEnd::vibrate(), qui utilise les classes VibrationEffectProxy, VibratorManagerProxy et VibratorProxy pour générer la vibration.

Notifications

L'envoi d'une notification est géré par la fonction ` BackEnd::notfy() ` de ` Backend `, à l'aide de la classe ` NotificationManagerProxy `.

La notification est déjà créée lors de l'initialisation de la classe ` Backend ` dans la fonction ` BackEnd::createNotification() `.

Réglage de la luminosité

Le réglage de la luminosité à l'aide du curseur de luminosité de l'interface utilisateur déclenche une modification de propriété dans la classe ` Backend `, qui sera gérée dans la fonction ` BackEnd::setBrightness() `. À cet endroit, la luminosité du système est ajustée à l'aide des classes ` SettingsProxy`, ` SystemProxy`, ` ContextProxy`, ` LayoutParamsProxy ` et ` WindowProxy `.

Le curseur de luminosité est synchronisé dynamiquement avec la luminosité du système ; ainsi, si la luminosité du système est modifiée depuis l'extérieur de l'application, le curseur s'ajuste pour refléter la luminosité actuelle du système.

Si l’application ne dispose pas de l’autorisation d’écrire dans les paramètres système, une fenêtre « Activity » s’ouvre via l’intent « ACTION_MANAGE_WRITE_SETTINGS ». Une fois cette fenêtre « Activity » ouverte, l’utilisateur doit manuellement accorder à l’application l’autorisation d’écrire dans les paramètres système.

Réglage du volume

Le réglage du volume à l’aide du curseur de volume dans l’interface utilisateur déclenche une modification de propriété dans la classe ` Backend `, qui sera gérée par la fonction ` BackEnd::setVolume() `. C’est là que le volume système est réglé à l’aide de la classe ` AudioManagerProxy `.

Le curseur de volume est synchronisé dynamiquement avec le volume musical du système ; ainsi, si le volume musical du système est modifié depuis l'extérieur de l'application, le curseur de volume s'ajuste pour refléter le volume musical actuel du système.

Exemple de projet sur code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.