On this page

Qt Bridges - Java

This document outlines the process required to set up the development environment for Java Bridge. Java Bridge allows applications to bridge Java code to QML. The bridging is based on main two mechanisms:

  • Java JNI (C++) native code to do the actual bridging. JNI allows the bridge to translate data and function calls between QML and Java
  • KSP (Kotlin Symbol Processing) for processing the user's classes and annotations at build-time. KSP allows the bridge to introspect user-code and generate all needed bridging code

The main parts of the solution are:

  • User code (application)
  • Java Bridge Java files in a JAR file (.jar)
  • Java Bridge compiled native (C++) plugin (.dylib/.so/.dll)
  • Maven gradle plugin for shipping to end users (not done yet, see End-user workflow)
  • Qt Libraries

Getting started

A pre-requisiste for using the Quick Bridge is to set up a Java/Kotlin project normally. Qt provides a Gradle plugin that is hosted in Maven central, and you need to declare this in the application project Gradle files. Ideally that should be all that is required for setting up the project:

// settings.gradle.kts
    pluginManagement {
    repositories {
       gradlePluginPortal()
    mavenCentral()
      }
   }
// build.gradle.kts
plugins {
   id("org.qt.qt-bridge") version "0.1.0"
}

qtBridge {
   // options like commercial vs. OSS Qt
}

Installation

Gradle plugin and Maven artifacts

The Gradle plugin is a small plugin that, among other things, detects the operating system. It pulls in the needed artifacts as Maven artifacts:

  • QML Bridge's Java/Kotlin classes (JAR)
  • QML Bridge's native library
  • Qt Libraries

Alternatively, Qt Libraries may be served directly from the Qt download site, thus avoiding the need to upload them on Maven.

Note: If using Maven or the Gradle plugin is not possible for any reason, Qt can provide one large JAR file that contains the Bridge classes, Qt libraries, and Bridge native libraries.

Setting up development environment

QML syntax highlighting

The QML Language Server is not part of the Maven deliverable. To turn on QML syntax highlighting you need to install Jetbrains' QML LSP plugin from Jetbrains Marketplace. You can Install it via standard Intellij IDEA menus, or directly from the marketplace website.

Starting from Intellij IDEA version 2025.3, whose estimated release date is in December 2025, the QML plugin works with free versions of Intellij IDEA.

Bridge building and development

For building the Java Bridge two environments are needed:

  • Java Environment: Java Development Kit (JDK) for compiling Java code, and using JNI bridge to facilitate communication between Java and native C++ methods.
  • C++ Environment: CMake and a C++ compiler for configuring and compiling the native library
Java environment

The Java Compiler (javac) is a key tool for compiling Java source code into bytecode that can be executed by the Java Virtual Machine (JVM). Follow these steps to set up your Java environment:

Install the Java Development Kit (JDK):

  • Download and install the JDK from the Oracle website or use a package manager for your operating system.
  • Ensure that the JAVA_HOME environment variable points to your JDK installation directory.
  • Verify Installation by typing javac -version

Currently, the project uses Java version 21.

C++ environment
  • Install CMake: Follow the instructions on the CMake website to install CMake for your operating system.
  • Install a C++ Compiler: Follow the instructions of your {https://wiki.qt.io/Building_Qt_6_from_Git}{Qt version} to set up the proper toolchain.

Qt dependencies

To use Qt with JNI and Java, you'll need to install the necessary Qt libraries and configure them for your C++ project. At minimum the project needs QtCore and QtDeclarative modules.

Current implementation is not bound to any specific Qt version, and versions starting from Qt 6.8 are expected to work. It is, however, worth mentioning that Java bridge uses private Qt APIs to build and register metaobjects. While those APIs are fairly stable, it may also break portability.

@QMLRegistrable

@QMLRegistrable annotation marks classes for bridging to QML. Both singletons and regular QML instantiable types can be registered. The annotation is analogous with having a C++ QObject that defines a QML_ELEMENT macro.

The Following shows the possible composition of such class:

@QMLRegistrable(name, module, singleton)
├── QtProperty<type> (usually many)
├── QtListModel<type> (usually one or none)
├── All public Methods are registered as invokable functions (usually many)
└── @QMLSignals (signal interface) (one interface with many signals)
Properties (QtProperty)

Properties are represented by instances of QtProperty. A QtProperty wraps a value and automatically notifies QML when the value changes. Updates from either the Java-side or the QML-side are reflected on the other side. QtProperty supports basic boxed datatypes (Integer, String, ...), Java Collections (lists), and Enums.

For observing changes on the QML-side, normal QML bindings and signal catching mechanisms work. On the Java-side, QtProperty the property value-change observation is provided with a callback mechanism (onValueChanged).

List model (QtListModel)

Java bridge provides an end-user API for bridging lists that can be used as list models on the QML side. QtListModel is a regular Java class with Java-like interface for storing items on a list. Under the hood, this list is bridged to QML as a QAbstractListModel. Editing is possible from both Java- and QML side.

Public methods as invokable functions

By default, all public methods defined in a @QMLRegistrable class are automatically registered as QML invokable slots. This means that any public method (without needing additional annotations) can be called from QML.

Signals (Using @QMLSignals)

Instead of manually writing and managing signal methods inside your class, define a separate Java interface to represent your signals.

Running

Building from source

The project uses Gradle as the build system. First, create a Gradle wrapper:

gradlew wrapper

List all tasks with:

./gradlew tasks

Gradle-task that builds everything and runs tests:

./gradlew check

In order to include benchmarks during tests run:

./gradlew check -Pbenchmark

Gradle-task that runs all example applications:

./gradlew runQtExamples

Or just a single application:

./gradlew fruitmanager

Generate the documentation:

./gradlew docs:all

Note: Building the javadoc target instead of docs:all would build incomplete Java documentation.

QtQuickApplication

QtQuickApplication is the entrypoint for QML bridge execution. Its usage is:

 import org.qt.bridge.app.QtQuickApplication;

 public class Main {

 public static void main(String[] args) {
     final QtQuickApplication app = new QtQuickApplication(args);
     app.execute();
   }
}

Tutorials

The following snippet illustrates a simple Java-side example:

 // Registers this class as QML singleton
 @QMLRegistrable(singleton = true)
 public class FruitBasket {
 // Establishes a binding to a callback interface that is used to emit signals or notifications
 // from Java to QML. This allows QML to react to specific events like validation failures or updates.
 public interface QmlCallback {
     void basketSold(Integer price);
     void basketStolen();
 }
 @QMLSignals
 QmlCallback qmlCallback;

 // A QtListModel of strings, bridged to QML as a QAbstractListModel.
 // This allows it to be used in model-driven QML components
 public final QtListModel<String> fruitList =
         new QtListModel<>(new ArrayList<>(Arrays.asList("Mango", "Kiwi")));

 // An Integer bridged to QML. Can be used on QML-side as a standard read-write property
 public QtProperty<Integer> fruitBasketPrice = new QtProperty<>(24);

 {
     // Observe fruitList changes
     fruitList.onSizeChanged(() -> System.out.println("List size changed to: " + fruitList.size()));
     // Observe price changes
     fruitBasketPrice.onValueChanged(() -> System.out.println("Fruit basket price changed"));
 }

 // Function that is invokable from QML
 public void sellAllFruits() {
     System.out.println("Selling all fruits");
     // Inform QML that sale was a success
     qmlCallback.basketSold(25);
 }
}

© 2025 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.