Skip to content

cpp: add Android backend support#11628

Draft
ruminorix wants to merge 13 commits into
slint-ui:masterfrom
ruminorix:feature/cpp-android-backend
Draft

cpp: add Android backend support#11628
ruminorix wants to merge 13 commits into
slint-ui:masterfrom
ruminorix:feature/cpp-android-backend

Conversation

@ruminorix
Copy link
Copy Markdown

Summary

Enable C++ developers to build Slint applications for Android, closing #6281.

  • Add backend-android-activity feature to slint-cpp with android_main bridge that calls user's extern "C" slint_main()
  • CMake auto-configuration: detects NDK toolchain, maps ANDROID_ABI to Rust targets, enables Skia renderer, propagates NDK/SDK env vars
  • Gradle template project (api/cpp/android/) for easy project bootstrapping
  • Android build of the C++ todo example (examples/todo/cpp/android/), tested as a working APK on a real device
  • Full documentation: prerequisites, project setup, building, deploying, JNI interop, troubleshooting
  • Updates existing mobile docs to remove the "Rust only" limitation

Also fixes two pre-existing issues blocking C++ Android compilation:

  • slint_callbacks.h: std::apply with const tuple ref fails on NDK r27's stricter libc++; replaced with custom detail::apply
  • cbindgen.rs: Option<Timer> was forward-declared but never defined for Android-only ContextMenu::long_press_timer field

Test plan

  • Built the todo example APK for arm64-v8a and tested on a real Android device
  • Verify Gradle template project builds standalone (with FetchContent from git)
  • Test on x86_64 emulator
  • CI integration (future work)

Enable C++ developers to build Slint applications for Android using
the standard Gradle + CMake workflow or standalone CMake with the NDK
toolchain.

The implementation adds:
- `backend-android-activity` feature to slint-cpp that wires up the
  existing Rust Android backend and provides an `android_main` entry
  point which calls `extern "C" slint_main()` in user C++ code
- CMake auto-configuration that detects the Android NDK toolchain,
  maps ANDROID_ABI to Rust targets, enables Skia renderer, and
  propagates NDK/SDK environment variables to the Rust build
- A Gradle template project (api/cpp/android/) similar to the ESP-IDF
  template, with AndroidManifest.xml, build.gradle.kts, and a minimal
  hello-world example
- An Android build of the C++ todo example (examples/todo/cpp/android/)
  that has been tested as a working APK on a real device
- Documentation covering prerequisites, project setup, building,
  deploying, JNI interop, and troubleshooting
- Updates to existing mobile docs removing the "Rust only" limitation

Also fixes two pre-existing issues that prevented C++ compilation with
Android NDK r27:
- slint_callbacks.h: std::apply with const tuple& fails on NDK r27's
  strict libc++; replaced with a custom detail::apply helper
- cbindgen.rs: Option<Timer> was forward-declared but never defined for
  Android-only ContextMenu::long_press_timer field

Issue: slint-ui#6281
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Comment thread ANDROID_CPP.md Outdated
Comment thread api/cpp/include/private/slint_callbacks.h
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread examples/todo/cpp/android/app/src/main/cpp/main.cpp Outdated
Comment thread api/cpp/Cargo.toml Outdated
Comment thread api/cpp/cbindgen.rs Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android-cpp.mdx Outdated
@ogoffart
Copy link
Copy Markdown
Member

ogoffart commented May 5, 2026

Can you also port the usecase demo to android, and have it build by the CI.

ruminorix and others added 5 commits May 5, 2026 16:26
- Fix CI: add missing backend_android_activity field in xtask/cppdocs.rs
- Fix Timer binary compatibility: use uintptr_t instead of uint64_t in
  C++ Timer, remove Option wrapper from ContextMenu::long_press_timer
- Remove Option<T> template hack from cbindgen.rs
- Add comment explaining custom detail::apply (NDK r27 compat)
- Merge android-cpp.mdx into android.mdx using Tabs for Rust/C++
- Share todo example main.cpp/CMakeLists.txt between desktop and Android
- Port usecases demo to Android with gradle project
- Add android_cpp CI job for cross-compiling usecases demo
- Remove ANDROID_CPP.md from tracked files
- Add note that backend-android-activity is auto-enabled by CMake
- Add .kts and .properties to xtask license header checker
- Add SPDX headers to gradle-wrapper.properties files
- Add SPDX header to todo AndroidManifest.xml
- Add REUSE.toml annotations for gradle-wrapper.properties files
Build Slint from the top-level CMakeLists.txt with SLINT_BUILD_EXAMPLES
for the android_cpp CI job so that the Slint target is available when
building the usecases demo. Also add the usecases subdirectory to
demos/CMakeLists.txt and fix cSpell unknown words (armeabi, androideabi)
in the Android docs.
Comment thread api/cpp/cbindgen.rs Outdated
Comment thread api/cpp/Cargo.toml Outdated
Comment thread api/cpp/CMakeLists.txt Outdated

namespace detail {
// Custom apply implementation to replace std::apply.
// NDK r27's libc++ rejects std::apply with const tuple references in some configurations.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? is that some specific version of libc++? is there some issue for that?
Could there be another fix that doesn't need this workaround?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an issue with NDK r27's libc++ (the version shipped with the current LTS NDK). When std::apply is called with a const std::tuple&, it fails to compile because their implementation tries to use std::get on a non-forwarding reference in a way that triggers a substitution failure.

I don't have a specific upstream issue link for this — I hit it during testing with NDK r27 (27.2.12829862). The custom detail::apply is a straightforward reimplementation that avoids the problematic code path.

An alternative would be to avoid passing const tuple refs to std::apply in the call sites, but that would require changing the Callback implementation more invasively. This seemed like the least disruptive fix. Happy to explore other approaches if you'd prefer.

Comment thread api/cpp/android/README.md Outdated
@ruminorix
Copy link
Copy Markdown
Author

Done — the usecases demo has been ported to Android with a full gradle project in demos/usecases/cpp/android/, and an android_cpp CI job was added to cross-compile it.

ruminorix added 3 commits May 9, 2026 06:33
The backend-android-activity Cargo feature is unnecessary. The
i-slint-backend-android-activity dependency is now unconditionally
included on Android via target cfg, and the selector feature is
enabled unconditionally (no-op on non-Android). CMake no longer
needs to force-enable the feature.
The template will be set up as a branch in slint-ui/slint-cpp-template
instead, per review feedback.
Timer is already visible inside slint::cbindgen_private through
normal C++ name lookup in the enclosing slint namespace.
Comment thread REUSE.toml Outdated
Comment thread demos/usecases/cpp/android/app/src/main/cpp/CMakeLists.txt Outdated
Comment thread demos/CMakeLists.txt
Comment thread examples/todo/cpp/android/app/src/main/cpp/CMakeLists.txt Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a maintenance point of view, i wonder if this is good to have this kind of file repeated for all our example. And when do we need to update this and other stuff. Could you suggest easier method than having all these files?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main files that get duplicated are: gradle-wrapper.properties (pinned gradle version), gradle.properties (just android.useAndroidX=true), settings.gradle.kts (project name), and build.gradle.kts (AGP plugin version). These are fairly stable — gradle version bumps happen infrequently.

One option to reduce duplication: a shared top-level android/ directory with a single gradle project that builds all examples as subprojects, each referencing their root CMakeLists.txt. This would consolidate the gradle wrapper and AGP version in one place. Want me to try that approach?

- Remove redundant android-specific CMakeLists.txt files; root CMakeLists
  now supports SLINT_SOURCE_DIR for building Slint from source via gradle
- Update gradle build.gradle.kts to reference root CMakeLists
- Invert directory tree in docs: C++ code at root, android/ as subdirectory
- Remove outdated api/cpp/android glob from REUSE.toml
Comment thread demos/usecases/cpp/CMakeLists.txt Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
- Move Rust API docs link inside Rust tab
- Simplify C++ section to reference existing CMake docs
- Remove redundant .slint file example
- Simplify slint_main documentation
- Streamline project structure description
Comment thread api/cpp/CMakeLists.txt
endif()

# Android requires a shared library
set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if these defaults could be set at the source instead of here with these kind of ugly overrides. We're in the right file, aren't we?:)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Although these are not default but mandatory features

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment to say "Android requires the skia renderer and cannot use desktop backends".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could integrate it into the define_cargo_dependent_feature calls by adding AND NOT ANDROID to the depends_condition for winit/qt/femtovg, and using if(ANDROID) to flip the renderer-skia default to ON. The issue is that these are mandatory for Android, not just defaults — with the current approach, FORCE prevents the user from accidentally enabling winit on Android. With the depends_condition approach, we get the same effect since cmake_dependent_option forces the value OFF when the condition is false.

Want me to try that approach? It would replace the FORCE overrides with conditions on the feature definitions themselves.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If mandatory perhaps that should become an option in the macro/function. But IMO it's sufficient if defaults are on/off correctly and it's all in one place.

My concern is that if we remove/rename the feature we need to look at multiple places.

(Not a super big issue but worth research if it can be done better)

Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread api/cpp/CMakeLists.txt
endif()

# Android requires a shared library
set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Although these are not default but mandatory features

Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
Comment thread docs/astro/src/content/docs/guide/platforms/mobile/android.mdx Outdated
ruminorix added 2 commits May 9, 2026 11:35
- Flatten usecases gradle project: remove app/ submodule, merge into
  single-module project with all config in root build.gradle.kts
- Update docs to show flattened android project structure with full
  file contents for build.gradle.kts and AndroidManifest.xml
- Move Rust API docs link to page intro (not Rust-specific section)
- Fix CMakeLists comment: android features are mandatory, not defaults
- Revert SLINT_SOURCE_DIR addition from example CMakeLists files,
  keeping them simple with just find_package(Slint REQUIRED)
- Fix broken docs link: getting_started → getting-started
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants