Copyright (C) 2025 The Qt Company Ltd. SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
- Contents:
- Introduction
- Get in touch
- Status
- Early Preview Quick Start
- macOS
- Linux
- Windows
- Troubleshooting
- End-user workflow
- Description
- Quick start
- Gradle Plugin and Maven Artifacts
- Bridge Building and Development
- Java Environment
- C++ Environment
- Qt dependencies
- Building and running the Project
- Java Bridge API Overview
- Classes and Annotations
- Example Code
- Terms and Conditions
- Additional Terms and Conditions
This documentation outlines the process required to set up the development environment for Java/Kotlin Bridge. The Bridge allows applications to bridge Java and Kotlin code to QML. The bridging is based on two main 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
- Qt Libraries
Bridge for Java/Kotlin is currently in early preview, and in active development. It can be compiled, run, and tested out on the major desktop platforms. Notable limitations include:
- You need to set up Bridge development environment to use it, instead of relying the Bridge Gradle plugin to download necessary components. See Early Preview Quick Start for setting up the environment
- APIs may change or even be removed
- There are many known issues and missing features
You can reach us in the Qt Forum, specifically in the Qt Bridges category. For Qt bug tracker users there's also the JavaQt Bridge task.
This chapter provides hands-on instructions for setting up the development environment. This setup is needed only for the time being - in future the needed components will be downloaded automatically by the Qt Bridge Gradle plugin.
Supported platforms:
- macOS (
arm64,x86_64), - Linux (
arm64,x86_64), - Windows (
arm64,x64)
The needed components are:
- This repository i)
- Qt 6.10+ (Qt 6.8, Qt 6.5) ii)
- Gradle 8.14.2+
- CMake 3.16+
- C++ Toolchain
- OpenJDK 21
i) Clone this repository
git clone https://code.qt.io/qt/qtbridge-java.git/
cd qtbridge-javaii) To get Qt please see Qt Download Page, or compile it from sources. Qt 6.10 is the required minimum and tested version, but for early experimentation purposes Qt 6.8 and Qt 6.5 should work too
Following are example command line instructions for different platforms, adjust as needed. It is also possible to use an IDE for development. For this purpose we've tested VS Code and Intellij IDEA. Their setup is not covered here though. In summary you'll open the top level directory as a Gradle folder (VS Code) / project (IntelliJ), and make sure you have needed environment configured.
# Ensure Qt is on PATH or Qt6_DIR is set, adjust as needed
export PATH=~/Qt/6.10.1/macos/bin:$PATH
# or
export Qt6_DIR=~/Qt/6.10.1/macos
# Ensure C++ toolchain is installed
xcode-select --install
# Verify C++ toolchain installation
clang++ --version
# Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install JDK
brew install openjdk@21
export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home
export PATH="$JAVA_HOME/bin:$PATH"
# Verify JDK installation
javac --version
# Install Gradle
brew install gradle
# Test Gradle installation
gradle --version
# Generate Gradle wrapper (can take a long time on first run)
gradle wrapper
# Run an example application
./gradlew colorpaletteclientThese instructions are on Ubuntu 24.04 arm64.
# Ensure Qt is on PATH or Qt6_DIR is set, adjust as needed
export PATH=~/Qt/6.10.1/gcc_arm64/bin:$PATH
# or
export Qt6_DIR=~/Qt/6.10.1/gcc_arm64
# Ensure build tools and other essential packages are installed
sudo apt install build-essential cmake gradle openjdk-21-jdk
gcc --version
gradle --version
cmake --version
javac --version
# Ensure right Java is used (in case system has multiple)
readlink -f "$(which javac)" # For checking which Java is on PATH
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-arm64
# Generate Gradle wrapper (can take a long time on first run)
gradle wrapper
# Run an example application
./gradlew colorpaletteclient# Install C++ toolchain:
https://visualstudio.microsoft.com/downloads/
# Install Gradle, for example:
https://gradle.org/install/
# Install CMake, for example:
https://cmake.org/download/
# Install OpenJDK, for example:
https://learn.microsoft.com/en-us/java/openjdk/download
# Set needed directories on PATH (adjust paths)
SET PATH=C:\path\to\CMake\bin;%PATH%
SET JAVA_HOME=C:\path\to\jdk-21
SET PATH=%JAVA_HOME%\bin;%PATH%
SET PATH=C:\path\to\gradle-9.2.1\bin;%PATH%
SET PATH=%USERPROFILE%\Qt\6.10.1\msvc2022_arm64\bin;%PATH%
# Set up C++ environment, for example (adjust path as needed)
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" arm64
# Verify they're accessible
where cl
gradle --version
javac --version
qmake --version
# Generate Gradle wrapper (can take a long time on first run)
gradle wrapper
# Run an example application
gradlew colorpaletteclient
Use Gradle clean to remove earlier builds
gradle cleanIf Gradle stops finding for example CMake, AWT, or Qt, sometimes it helps to restart the Gradle daemons on the terminal which has the right environment variables set. Gradle daemon is a long-lived background process and stopping it forces it to restart with the right environment:
gradle --stopBridge plugin may have also downloaded libraries in a cache which may cause confusion
rm -fr ~/.gradle/caches/qt-downloadssudo apt install libgl1-mesa-dev libvulkan-dev vulkan-toolsEffortless first development experience for non-Qt developers is a priority for Qt Bridges. With Java Bridge, the plan is to publish a Maven Gradle plugin and related artifacts either on Maven or on Qt download site.
Qt Bridge allows any developer to set up their Java/Kotlin project using the standard Gradle workflow. Qt provides a Gradle plugin hosted on Maven Central that integrates seamlessly into your existing project structure.
The setup process is designed to be minimal. Developers only need to declare the Qt Bridge plugin in their application's Gradle files. The plugin handles everything else automatically, including downloading and configuring the environment.
To quickly get started with a new QtBridge application, you can use the included starter example:
See the starter Project Quickstart
Add the Qt Bridge Gradle plugin to your project by updating the following files:
pluginManagement {
repositories {
gradlePluginPortal()
maven { url = uri("https://android.qt.io/maven/releases") }
}
}Using Maven Local:
If you are working with a locally published version of the Qt Bridge Gradle plugin and qmlbridge module (for development or testing), add mavenLocal() to the repositories:
pluginManagement {
repositories {
gradlePluginPortal()
mavenLocal()
}
}For more information about publishing and developing Qt Bridge modules, see the Qt dev Gradle plugins.
plugins {
id("org.qtproject.qt.bridge.qtbridge-plugin") version "0.1"
}
qtBridge {
application {
name = "myApp" // optional: creates running task with this name (defaults to project's name)
mainClass = "com.example.MyQtApplication" // optional: if set, creates run task
jvmArgs.addAll("-Xmx512m") // additional JVM arguments may be passed like this
}
qml {
entryPoint = "App.qml" // optional: auto-discovers Main.qml or main.qml if not set
sourcePath = "src/ui/qml" // default: "src/main/qml"
importPaths.from(file("/workspace/custom/qml")) // optional: for adding additional QML modules
}
// optional configuration
qtLibraryPath = "/path/to/Qt/libs"
qtBridgeLibraryPath = "/path/to/bridge/native/lib"
}If you provide a name and mainClass in the configuration above, you can launch your app directly from the terminal:
./gradlew myAppThe Qt Bridge plugin core functions are:
- Detecting the host OS and architecture
- Resolving and downloading the correct Qt and Qt Bridge artifacts if not specified
- Configuring the application entry point (main class, JVM args)
- Managing QML source folder, imports, and automatic main QML discovery
- Providing optional overrides for Qt and native library paths
The plugin manages three critical components to ensure your application runs seamlessly across different environments:
| Artifact | Description | Selection Logic |
|---|---|---|
| Qt Bridge SDK (JAR) | Java/Kotlin API classes | Latest version (currently 0.1) |
| Qt Bridge native library | Platform-specific native binary | Automatically matches host OS/Arch |
| Qt Framework libraries | Required shared Qt libraries | Defaults to 6.10.0 |
- Version mismatch: The plugin ensures stability by downloading synchronized versions of the Qt framework and
the Bridge native library (currently Qt 6.10.0). However, if you manually specify a local path for one (using
qtLibraryPathorqtBridgeLibraryPath) while letting the plugin download the other from the server, you risk a version mismatch. - Platform support limitations: The plugin currently supports the following environments for automatic artifact downloading:
- macOS: Apple Silicon (arm64)
- Linux: x86_64 (Tested on Ubuntu 24.04)
- Windows: Support coming soon.
For building 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 C++ compiler for configuring and compiling the native library
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_HOMEenvironment variable points to your JDK installation directory.
- Verify Installation:
javac -version
Currently, we use Java version 21 for the project.
- 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 Qt version to set up the proper toolchain.
For CMake and C++ compiler versions follow the instructions of your Qt version.
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.
This project uses Gradle as the build system. All commands should be run from the project root directory using the
wrapper (./gradlew).
| Command | Description |
|---|---|
gradle wrapper |
Creates the Gradle wrapper scripts (gradlew/gradlew.bat). |
./gradlew tasks |
Lists all available tasks within the project. |
./gradlew check |
Builds all artifacts, compiles the native bridge library, and runs all unit tests. |
./gradlew docs:all |
Builds java documentation |
The project relies on a platform-specific native library (the QtBridge) for runtime execution. This library must be located where the Java/Kotlin runtime can find it.
Ensure the directory specified for library lookup contains the correct file for your operating system:
| Platform | Required Library File | Example Path |
|---|---|---|
| Windows | QtBridgeNative.dll |
C:\QtBridge\lib |
| macOS | libQtBridgeNative.dylib |
/Users/username/QtBridge/lib/mac-arm64 |
| Linux | libQtBridgeNative.so |
/home/username/QtBridge/lib/x86_64 |
If you have already built the native library, inform the JVM of its path using a system property or environment variable.
| Option | Description | Example Command |
|---|---|---|
| System Property | -Dqtbridge.native.dir=<path> |
./gradlew check -Dqtbridge.native.dir=path/to/lib/mac-arm64 |
| Env variable | export QTBRIDGE_NATIVE_DIR=<path> |
export QTBRIDGE_NATIVE_DIR=path/to/lib/mac-arm64; ./gradlew check |
Example applications (e.g., colorpaletteclient) support automatic library management:
- Production Mode: is when an application is using Qt Bridge plugin from MavenCentral or other different source. If the needed libraries (qt and qt bridge native) are missing, they will be automatically downloaded.
- Development Mode: is when an example application is running within this repository. In other words, using the local qmlbridge and the local qtbrige-plugin
| Mode | Command | Behavior |
|---|---|---|
| Production | ./gradlew colorpaletteclient |
The plugin downloads the native library if missing, then runs. |
| Production & Development | ./gradlew colorpaletteclient -Dqtbridge.native.dir=path/to/lib |
Runs application with manually specified native library. |
| Development | ./gradlew colorpaletteclient |
Builds the native library from source, then runs. |
| Task | Configuration | Command Example |
|---|---|---|
| Run benchmarks | Enables benchmark tests using the -Pbenchmark property. | ./gradlew check -Pbenchmark |
| Benchmarks with Path | Uses a manually specified qt bridge native library path. | ./gradlew check -Pbenchmark -Dqtbridge.native.dir=path/to/lib |
| Example in dev mode but force non-dev | Forces the plugin to download qt and qt bridge libraries if missing. | ./gradlew colorpaletteclient -Pqt.plugin.mode="non-dev" |
| Option | Type | Target | Description |
|---|---|---|---|
-Dqtbridge.native.dir=<path> |
System Property | check task and example applications |
Specifies the directory containing the compiled native library. |
QTBRIDGE_NATIVE_DIR=<path> |
Environment Variable | check task and example applications |
Specifies the directory containing the compiled native library. |
-Pbenchmark |
Project Property | check task |
Enables benchmark tests during execution. |
-Pqt.plugin.mode |
Project Property | Examples tasks or any project that applies qtbridge-plugin | Allows downloading qt bridge and qt libraries if missing in the caller project |
QtQuickApplication is the entrypoint for QML bridge execution. Its usage is:
import org.qtbridge.app.QtQuickApplication;
public class Main {
public static void main(String[] args) {
final QtQuickApplication app = new QtQuickApplication(args);
app.execute();
}
}@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 which defines a QML_ELEMENT macro.
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)
└── @QMLComplete (optional, on completion handler annotation)
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).
Java bridge provides an end-user API for bridging lists which 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.
By default, all public methods defined in a @QMLRegistrableclass are automatically registered
as QML invokable slots. This means that any public method (without needing additional annotations)
can be called from QML.
Instead of manually writing and managing signal methods inside your class, you define a separate Java interface representing your signals.
@QMLComplete marks a method to run after the QML engine finishes creating a @QMLRegistrable instance. The handler is
called for objects instantiated by QML. It is not invoked for singletons or for instances you create manually on the Java side.
The annotated method:
- Must take no parameters and must return void
- Must appear at most once per
QMLRegistrable-annotated class
Following 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);
}
}If you, your employer, or the legal entity you act on behalf of hold commercial license(s) with a Qt Group entity, Qt Bridges constitutes Pre-Release Code under the Qt License/Frame Agreement governing those licenses, and that agreement's terms and conditions relating to Pre-Release Code apply to your use of Qt Bridges as found in this repo. This Qt Bridges repo may provide links or access to third-party libraries or code (collectively "Third-Party Software") to implement various functions. Use or distribution of Third-Party Software is discretionary and in all respects subject to applicable license terms of applicable third-party right holders.
The Qt Bridge for Java is built using the OpenJDK (https://openjdk.org) and Kotlin (https://kotlinlang.org)
OpenJDK is licensed under the GNU General Public License, version 2, with the Classpath Exception.
Kotlin SDK and runtime are licensed under the Apache License, version 2.0
No modifications were made to the OpenJDK source code, and Qt Bridge for Java does not require any modifications.
This project is not affiliated with or endorsed by Oracle and/or its affiliates.
"Java" and "OpenJDK" are trademarks or registered trademarks of Oracle and/or its affiliates.