An existing desktop application written with Swing and AWT, with the SwingX component set, or with JavaFX can build and run as a Codename One application without changes to its Java code. The desktop sources go into a Codename One project, the build maps them onto Codename One’s implementation of the java.awt, javax.swing, org.jdesktop.swingx and javafx.* APIs, and the result runs on every Codename One target: iOS, Android, the web, the native desktop ports and the simulator.

No desktop Java runtime is involved. Buttons, text fields, check boxes, sliders and tabs are Codename One components that take the platform’s theme, layout managers and layout panes run the rules they run on the desktop, and code that paints with Graphics2D or a JavaFX Canvas draws through Codename One’s graphics.

Motivation

A Swing or JavaFX application that already works has three reasons to move.

It reaches every platform from one code base. The same sources build for iPhone and iPad, Android phones and tablets, the browser, and native Linux, macOS and Windows. Nothing is rewritten per platform, and the desktop version keeps its windows, menus, and keyboard.

It ships as one native program. A native desktop build is a single executable with no Java runtime to bundle, install, or update. It starts faster, holds less memory and downloads smaller than the same application with a trimmed Java runtime beside it. Size, memory and start-up measures all three for eight applications, built both ways.

It gets the APIs desktop Java never had. The desktop sources compile next to the Codename One API and can call it directly, as Calling Codename One from desktop code shows. That fixes several of the oldest pain points of Swing and JavaFX development:

What a desktop developer reaches forWhat Codename One has

A camera, a microphone, the photo library

Capture takes photos, video and audio with the device’s camera and microphone. See Capture - photos, video, audio.

An embedded browser without a heavyweight engine to ship

BrowserComponent is the platform’s own web view, with a two-way bridge to JavaScript. See BrowserComponent & WebBrowser.

A local database

SQLite on every platform through Database, and an annotation-driven ORM over it. See SQL and Annotation ORM Section.

JSON without a reflection library

@Mapped classes and Mappers convert objects to JSON and XML with code generated at build time. See Annotation Mapping Section.

REST clients and OpenAPI

Rest for ad hoc calls, typed @RestClient interfaces, and cn1:generate-openapi to generate a client from an OpenAPI (Swagger) specification. See Rest API and Generate OpenAPI client (generate-openapi).

A server in the same language

A Java backend that shares its contract and data classes with the app. See Server-side backend and Backend controllers, contracts and WebSockets.

Styling without look-and-feel code

CSS compiled into a theme at build time, and applied live in the simulator as you save. See Styling and theming and CSS.

AI agents that can work with the app

Over the Model Context Protocol (MCP), an agent reads and drives a development build’s user interface, and a backend publishes tools for agents in production. See MCP Headless API and Backend MCP and the agent loop.

Push notifications, biometrics, maps, in-app purchase and the rest of the guide are available in the same way.

The layer implements the parts of each toolkit applications use, and the build tells you, file by file and line by line, about anything an application uses that it doesn’t implement (Unsupported API is a build error). New code can use either API: the two live in one project.

Quick start

Create a Codename One application project (for example from the initializr), then import the desktop project into it:

mvn cn1:import-desktop-project -Dcn1.desktop.import=/path/to/MyDesktopApp

The goal accepts a Maven or a Gradle project. It copies the project’s src/main/java, src/main/kotlin and src/main/resources into common/src/main/desktop, generates the Codename One main class that starts the application (The entry point), and lists what becomes of each of the project’s dependencies.

Two more parameters settle the cases the goal can’t decide for itself:

mvn cn1:import-desktop-project -Dcn1.desktop.import=/path/to/MyDesktopApp \
    -Dcn1.desktop.module=app \
    -Dcn1.desktop.mainClass=com.example.Main

cn1.desktop.module names the module to import when the project’s root has no sources and more than one of its modules does. cn1.desktop.mainClass names the class the application starts through when the project leaves more than one candidate. In both cases the goal stops before it copies anything and lists the choices.

After that, run and build as you would any Codename One application. The sources are the same for every target:

mvn cn1:run                                    # the simulator
mvn cn1:buildIos                               # iOS, on the build server
mvn cn1:buildAndroid                           # Android, on the build server
mvn cn1:buildJavascript                        # the JavaScript port
mvn package -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-source          # an Xcode project
mvn package -Dcodename1.platform=android -Dcodename1.buildTarget=android-source  # an Android Studio project

The simulator is the quickest place to see the application, and its skins show it at the size and density of a phone or a tablet. Every target and its options are in the Maven project workflow chapter.

Gradle

A Codename One project built with Gradle (see Gradle project workflow) takes the same sources in the same place, src/main/desktop. When that directory exists the Codename One Gradle plugin adds the toolkit layers and runs the same build steps, with no configuration:

./gradlew run            # the simulator
./gradlew buildIos       # an iOS build on the build server
./gradlew buildLinuxDevice

Placing the sources by hand

The directory mirrors a desktop project’s src/main:

common/src/main/desktop/
    java/        the desktop application's Java sources
    kotlin/      Kotlin sources, if the application has any
    resources/   images, .properties, .fxml and .css files

The common module’s desktop-compat profile activates when src/main/desktop exists and adds the Swing and JavaFX layers. Only the layer the compiled classes use ships, so a Swing application doesn’t carry the JavaFX runtime. A project created before the profile existed gets it from the import goal.

The entry point

A ported application is an ordinary Codename One application with an ordinary main class: a Lifecycle subclass in common/src/main/java. The import goal generates it, and its start-up calls the Swing application’s main, or launches the JavaFX Application, on the Codename One event dispatch thread (EDT). That thread is also the Swing event dispatch thread and the JavaFX application thread.

The class is yours. Edit it to pass arguments, to set display properties before the first window opens (Display properties), to show a Codename One screen first, or to start the desktop code only for some devices.

How the build works

Three ideas explain everything the build does:

  • The application is compiled once, against the desktop API, and relinked. Swing code compiles against the JDK’s own java.awt and javax.swing, JavaFX code against a javafx.* API that ships with Codename One, which is why the sources need no changes. The build then points every reference at Codename One’s implementation. No source is translated and nothing is interpreted: each target compiles the result as it compiles any Codename One application, to native code on iOS and the desktop ports, to Android’s bytecode on Android, to JavaScript in a browser.

  • Only what’s used ships. A layer is active only when the application’s classes name its API, and the native builds drop every class and method nothing reaches.

  • Nothing is found by reflection at run time. FXML documents, style sheets, resource lists and resource bundles are compiled into code by the build. That’s what makes the same application run on iOS, and it’s also why it starts fast everywhere.

The JDK API that ordinary application code uses is available on every target: java.io.File and java.nio.file, regular expressions, the stream API, java.time, the executors and concurrent collections, Optional, Properties, ResourceBundle, logging and preferences.

Unsupported API is a build error

The build checks every reference in the compiled application, so a class, method, constructor or field that a layer doesn’t have stops the build. It never becomes a failure on a user’s device. The message names the API the way the source spells it, with the source file and line:

Compliance check failed with 2 forbidden API references.
See /projects/orders/common/target/codenameone/compliance_check.txt for the full report.
First 2 violation(s):
 - com/example/OrdersPanel#restyleTree()V -> javax.swing.JTree.getUI() is not supported by the Codename One Swing compatibility layer (com/example/OrdersPanel.class) at OrdersPanel.java:48
 - com/example/NotesDialog#printNotes()V -> javax.swing.JTextArea.print() is not supported by the Codename One Swing compatibility layer (com/example/NotesDialog.class) at NotesDialog.java:31

A project with more than a handful of such references gets a summary that groups them by kind and by library, and points at the port report.

The port report

Every build of a project that uses a desktop layer writes target/codenameone/desktop-port-report.md, whether the check passes or fails. It says how many of the application’s calls into Swing, AWT and JavaFX are supported, lists what to change source file by source file with a Codename One alternative where one exists, and says what became of each dependency. Set cn1.desktop.report to write it elsewhere:

mvn package -Dcn1.desktop.report=port.md       # Maven
./gradlew build -Pcn1.desktop.report=port.md   # Gradle

To find out how well an application fits before changing a line of it, import it, build it and read the summary at the top of that report.

Porting a large application

A small application usually builds after the import. A large one usually needs some work, and widgets are seldom the reason: the user interface calls of a two-hundred-file Swing application, with FlatLaf for its look and a syntax-highlighting editor, built as written. The work is in the libraries and services around the user interface, and it goes in the order below.

Sort out the dependencies

The import keeps every dependency of the project. One that the layer implements itself — the JavaFX modules, SwingX, FlatLaf and RSyntaxTextArea — gets the provided scope: the sources compile against the real jar and the layer ships in its place.

Every other dependency is built with the application like any dependency of a Codename One application. Its classes aren’t relocated onto a layer, so a library written against Swing, AWT or JavaFX — a layout manager such as MigLayout, an icon library, a component set — fails the check for unsupported API. Replace it with its Codename One equivalent, or bring its source into src/main/desktop beside the application’s own, where it’s relinked and checked like the rest. A library that isn’t about user interfaces only has to use API a device has.

Use the Codename One equivalent

Where Codename One has an equivalent, the port report names it next to the finding. The common ones:

Desktop APIUse instead

java.net.http, HttpURLConnection

Rest or ConnectionRequest. For code written against java.net.URL, com.codename1.io.URL has the same shape, with openConnection and openStream. A REST API maps onto a typed @RestClient interface whose code is generated at build time, or onto one generated from its OpenAPI specification (Generate OpenAPI client (generate-openapi)).

ProcessBuilder, Runtime.exec

Supported on the desktop targets through Codename One’s process API, com.codename1.io.ProcessBuilder and Process, which has the JavaSE shape (Desktop and Backend Processes). Phones and the browser have no processes: to open a URL, a file or another application there, use CN.execute(url) after CN.canExecute(url), or Desktop.getDesktop().open(file) and browse(uri) from Swing code.

java.math.BigDecimal, BigInteger

com.codename1.util.BigDecimal and BigInteger

javax.crypto

The ciphers, MACs and key generators of com.codename1.security

java.sql

com.codename1.db.Database, SQLite on every platform, or the ORM over it (Annotation ORM Section)

Java serialization

Externalizable with Storage

Reflection over bean properties

PropertyBusinessObject. Codename One doesn’t support reflection: it defeats ahead-of-time compilation and obfuscation, which both need to see every class and member the code uses. Avoid it in new code.

A JSON library that binds by reflection

@Mapped classes with Mappers.toJson and Mappers.fromJson. The mapping code is generated at build time and uses no reflection, so it’s faster and smaller; see Annotation Mapping Section. JSONParser reads JSON with no model class at all.

A file watch service

Poll on a background thread with java.util.Timer.

JNA, System.loadLibrary

A NativeInterface with an implementation for each platform

Runtime.addShutdownHook

The stop and destroy methods of the application’s main class

Isolate what a phone can’t do

What remains is functionality a phone can’t provide in the same form. Put it behind an interface the application owns, keep the desktop implementation in the desktop project, and give the Codename One build an implementation that uses the Codename One API, a server, or nothing:

/// What the screens ask for. They hold one of these and nothing else.
public interface Tools {
    /// Whether the menu item that opens a terminal should be there at all.
    boolean canOpenTerminal();

    void openTerminal(String directory);

    void showInFileManager(String path);
}

/// The implementation the Codename One build uses. The desktop project
/// keeps its own, written with ProcessBuilder, in a class this build
/// doesn't compile.
public static class DeviceTools implements Tools {
    @Override
    public boolean canOpenTerminal() {
        return false;
    }

    @Override
    public void openTerminal(String directory) {
        throw new UnsupportedOperationException("No terminal on this device");
    }

    @Override
    public void showInFileManager(String path) {
        String url = path.startsWith("file:") ? path : "file://" + path;
        Boolean can = CN.canExecute(url);
        if (can == null || can.booleanValue()) {
            CN.execute(url);
        }
    }
}

This is where most of a large port’s time goes. An application whose screens call straight into its services needs that seam before the seam can get a second implementation. An application that already separates its screens from its services has it, and its port is mostly the first two steps.

Styling and theming

Codename One styles an application with CSS. A theme.css file in the project is compiled into a .res theme when the application is built, so the device reads a compact binary theme and never parses a style sheet. In the simulator the theme is rebuilt each time the file is saved, and the running application restyles at once. There is no look-and-feel class to write and no skin to subclass: every component has a UIID, and a CSS rule for that UIID styles it on every platform, with native themes for iOS, Android and the desktops underneath. The CSS chapter covers the syntax and Native modern themes the themes it builds on.

This differs from both desktop toolkits. A Swing look and feel is Java code, UI delegates that paint each widget, chosen at run time. JavaFX reads its CSS at run time and applies it to skins. In Codename One the styling is data, settled at build time, and the same for every platform.

A ported application uses this model in three ways.

Swing look and feel becomes a palette

The standard widgets are Codename One components: a JButton is a Codename One button, a JTextField is the platform’s text field with its own keyboard, selection and autocorrection, and the same holds for labels, check boxes, combo boxes, sliders, progress bars and tabs. They take the theme of the platform they run on.

UIManager.setLookAndFeel accepts any look and feel, so the usual call at the top of main runs unchanged and selects a light or a dark palette: FlatDarkLaf and other dark themes select the dark one, null or the system look and feel follows the platform’s own setting. Switching while windows are open restyles them at once. UIManager colors and fonts the application puts are kept and used, and the ones it reads answer from the palette.

FlatLaf’s entry points are part of the layer, so an application written against FlatLaf runs without the FlatLaf jar: setup(), the dark and light themes, the accent color, style classes and common client properties such as JTextField.placeholderText.

On a desktop, the desktop.themeMode build hint chooses the theme the packaged application uses, and native selects the theme of the machine it runs on:

codename1.arg.desktop.themeMode=native

The native macOS build reads macos.themeMode instead. Both are described under Desktop themes.

JavaFX style sheets compile with the application

JavaFX style sheets under src/main/desktop/resources are compiled when the application is built, like the application’s own CSS, into a compact table that the runtime applies. Type, class, id and pseudo-class selectors, the descendant and child relations, the cascade, inline setStyle, looked-up colors and @font-face work as in JavaFX:

.root {
    -app-accent: #2f6fed;
}

.card {
    -fx-background-color: white;
    -fx-border-color: -app-accent;
    -fx-border-width: 2;
    -fx-border-radius: 8;
    -fx-background-radius: 8;
}

#status {
    -fx-text-fill: -app-accent;
    -fx-font-size: 18;
    -fx-font-weight: bold;
}

.card > .button:hover {
    -fx-opacity: 0.85;
}

A property or selector the layer doesn’t style is a build warning with the file and line, and the rule is left out, which is what JavaFX itself does with a property it doesn’t know. Redefining a theme color such as -fx-base or -fx-accent recolors the controls the layer draws itself.

Codename One CSS styles the whole application

The application’s theme.css styles the Codename One components that the Swing and JavaFX widgets are, so one rule changes a button, a text field or a dialog across the whole application on every platform. Use it for anything that should look the same across screens; use the desktop styling for what’s particular to one screen.

Custom painting

A Swing component that overrides paintComponent paints as it does on the desktop, through a Graphics2D with shapes, strokes, paints, clipping and an affine transform:

public class BadgePanel extends JPanel {
    private String initial = "?";

    public void setInitial(String name) {
        initial = name == null || name.length() == 0 ? "?" : name.substring(0, 1);
        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(120, 120);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        int size = Math.min(getWidth(), getHeight()) - 16;
        int x = (getWidth() - size) / 2;
        int y = (getHeight() - size) / 2;
        g2.setColor(new Color(0x2f6fed));
        g2.fillOval(x, y, size, size);
        g2.setColor(Color.WHITE);
        g2.setFont(getFont().deriveFont(size / 2f));
        FontMetrics metrics = g2.getFontMetrics();
        g2.drawString(initial, (getWidth() - metrics.stringWidth(initial)) / 2,
                (getHeight() - metrics.getHeight()) / 2 + metrics.getAscent());
    }
}

In JavaFX, shapes, Text, paths, gradients, transforms and the shadow effects are drawn by the layer, and a Canvas replays its drawing calls when it paints, so it stays sharp at any display density.

On a phone and on the desktop

Logical pixels

Desktop code sizes things in pixels and assumes about 96 of them to the inch. The layers treat every Swing and JavaFX coordinate as a logical pixel of 1/96 inch and scale it to the display, so a 24 pixel row is the same physical height on a phone as on a desktop monitor. A phone is about 400 logical pixels across, so a layout designed for a 1024 pixel window is much wider than the screen. Layout managers and panes that adapt to the available width keep working; fixed sizes and null layouts need the attention they would need in a small desktop window.

A window whose content can’t shrink to the display keeps its minimum size and the user pans across it with a drag. A FlowLayout row and a horizontal JToolBar wrap instead of being cut off. A JavaFX scene whose root is a layout pane reflows to the screen as it does when its window is resized.

Windows, dialogs and menus

The first JFrame or Stage fills the screen. On the desktop ports each further frame or stage is a real window with the size, position and title the application gave it. On a phone or tablet a further frame is a form shown over the one before it, with a back command, and a dialog floats over the current form.

A modal dialog blocks its caller as it does on the desktop. JOptionPane.showMessageDialog, a modal JDialog, and showAndWait() on a JavaFX Alert all return after the user answers, while painting and input continue, so code written as a sequence of questions needs no restructuring:

import java.awt.BorderLayout;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class GreeterApp {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(GreeterApp::showFrame);
    }

    private static void showFrame() {
        JFrame frame = new JFrame("Greeter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTextField name = new JTextField(16);
        JPanel form = new JPanel(new GridLayout(0, 2, 8, 8));
        form.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
        form.add(new JLabel("Name"));
        form.add(name);

        BadgePanel badge = new BadgePanel();
        JButton greet = new JButton("Greet");
        greet.addActionListener(e -> {
            badge.setInitial(name.getText());
            JOptionPane.showMessageDialog(frame, "Hello, " + name.getText() + "!");
        });

        frame.getContentPane().add(form, BorderLayout.NORTH);
        frame.getContentPane().add(badge, BorderLayout.CENTER);
        frame.getContentPane().add(greet, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

On the desktop a JMenuBar is drawn in the window. On a phone its items become commands in the form’s overflow menu, and their accelerators become shortcuts.

Mouse, touch and keyboard

The same listeners run on every device:

InputOn a desktopOn a phone or tablet

Press, drag, release

The mouse, with its buttons and modifiers.

A finger, as the primary button.

Secondary click

The secondary button. JPopupMenu and ContextMenu open on it.

A long press opens the same menus.

Hover and wheel

Follow the pointer and the wheel.

None. Scroll panes follow a drag.

Touch events

None.

JavaFX receives a TouchEvent before each mouse event, and a SwipeEvent for a quick drag.

Keys

The keyboard.

The on-screen keyboard, typing into the text component being edited.

CN.isDesktop() answers which kind of device the application is on, so a feature that lives behind a hover or a key binding can offer a second way in on a phone.

Display properties

Three display properties adjust how a desktop application maps onto a device. Set them with CN.setProperty before the first window opens, for example in the generated main class or at the top of main:

if (!CN.isDesktop()) {
    // Fit a little more of a wide form on a phone.
    CN.setProperty("desktopcompat.scale", "1.6");
    // Keep the menu bar in the window instead of the overflow menu.
    CN.setProperty("desktopcompat.menuBar", "window");
}
// A fixed width font the application ships among its resources.
CN.setProperty("desktopcompat.monospaced", "DejaVu Sans Mono|DejaVuSansMono.ttf");

JFrame frame = new JFrame("Orders");
PropertyValueEffect

desktopcompat.scale

A number

The device pixels each logical pixel takes. Without it, the display’s density against 96 dots per inch.

desktopcompat.menuBar

window or commands

Draw a JMenuBar in the frame on every device, or turn its items into commands on every device.

desktopcompat.monospaced

Font Name|file.ttf

A fixed-width TrueType font the application ships, used for the Monospaced family.

Threading

The Swing event dispatch thread, the JavaFX application thread and the Codename One EDT are one thread. SwingUtilities.invokeLater, Platform.runLater, javax.swing.Timer and the JavaFX animation classes run there. Long work belongs on another thread, as on the desktop: SwingWorker and a JavaFX Task or Service run their work in the background and deliver results on the EDT:

new SwingWorker<List<String>, String>() {
    @Override
    protected List<String> doInBackground() {
        List<String> names = new ArrayList<String>();
        for (String name : new String[] {"Ada", "Grace", "Linus"}) {
            names.add(name);
            publish(name);
        }
        return names;
    }

    @Override
    protected void process(List<String> chunks) {
        for (String name : chunks) {
            model.addElement(name);
        }
    }

    @Override
    protected void done() {
        status.setText(model.getSize() + " names loaded");
    }
}.execute();

Resources and libraries

getClass().getResource(…​), getResourceAsStream(…​) and ResourceBundle.getBundle work: the build packages the files under src/main/desktop/resources and records where each one came from. ImageIcon, ImageIO.read and the JavaFX Image decode PNG and JPEG through the platform.

A library written against Swing or JavaFX, such as MigLayout or a component set, isn’t relinked onto the layer from its jar; see Porting a large application. The layer itself supplies FlatLaf and RSyntaxTextArea; keep those two dependencies as they are.

FXML

FXML documents are compiled when the application is built, into classes that build the same node tree with plain constructors and setters. The application loads a document the way it does on the desktop:

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("greeter.fxml"));
    Scene scene = new Scene(root);
    scene.getStylesheets().add(getClass().getResource("greeter.css").toExternalForm());
    stage.setTitle("Greeter");
    stage.setScene(scene);
    stage.show();
}
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns:fx="http://javafx.com/fxml/1" spacing="12" styleClass="card"
      fx:controller="com.codenameone.developerguide.desktopinterop.javafx.GreeterController">
    <padding>
        <Insets top="16" right="16" bottom="16" left="16"/>
    </padding>
    <Label fx:id="status" text="Who is there?"/>
    <TextField fx:id="name" promptText="Name"/>
    <Button fx:id="greet" text="Greet" onAction="#greet"/>
</VBox>

The controller’s @FXML fields and handlers can stay private, and initialize() runs after they’re set. The build writes direct code for the assignments, so no reflection is involved:

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class GreeterController {
    @FXML
    private Label status;

    @FXML
    private TextField name;

    @FXML
    private Button greet;

    @FXML
    private void initialize() {
        greet.disableProperty().bind(name.textProperty().isEmpty());
    }

    @FXML
    private void greet(ActionEvent event) {
        status.setText("Hello, " + name.getText() + "!");
    }
}

Imports, fx:include, fx:define, fx:root, %key strings from a resource bundle, @path locations and ${…​} bindings to a property all work, and so does an application’s own control used as an element. Because every class and property a document names is checked when it compiles, the build reports a typo with the file, line, and column, where the desktop would only find it when loading the document. A JavaFX application that builds its scene in code needs none of this:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CounterApp extends Application {
    private final IntegerProperty count = new SimpleIntegerProperty();

    @Override
    public void start(Stage stage) {
        Label label = new Label();
        label.textProperty().bind(Bindings.concat("Pressed ", count.asString(), " times"));

        Button press = new Button("Press");
        press.setOnAction(e -> count.set(count.get() + 1));

        VBox root = new VBox(12, label, press);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(16));

        stage.setTitle("Counter");
        stage.setScene(new Scene(root, 320, 200));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Supported API

The tables list the main classes of each area. The exact answer for an application is in its port report (The port report), which counts the calls that are supported.

Swing and AWT

AreaClasses

Windows and dialogs

JFrame, JDialog, JWindow, JOptionPane, JFileChooser, JColorChooser, ProgressMonitor, JRootPane and JLayeredPane

Internal frames

JDesktopPane and JInternalFrame

Containers

JPanel, JScrollPane, JSplitPane, JTabbedPane, JToolBar and Box

Buttons and labels

JLabel, JButton, JToggleButton, JCheckBox, JRadioButton, ButtonGroup and ImageIcon, with simple HTML in their text

Text

JTextField, JPasswordField, JFormattedTextField, JTextArea, JEditorPane, JTextPane with styled documents, document filters and listeners, and javax.swing.undo

Lists and values

JList, JComboBox, JSpinner, JSlider, JProgressBar and JSeparator, with their models and renderers

Tables and trees

JTable with javax.swing.table and TableRowSorter; JTree with javax.swing.tree

Menus and actions

JMenuBar, JMenu, the menu items, JPopupMenu, Action, KeyStroke, InputMap and ActionMap

Layout managers

BorderLayout, FlowLayout, GridLayout, GridBagLayout, CardLayout, BoxLayout, GroupLayout, SpringLayout and OverlayLayout

Borders

Every class of javax.swing.border, and BorderFactory

Painting

Graphics2D, Color, Font, BasicStroke, GradientPaint, AlphaComposite, the shapes and AffineTransform of java.awt.geom, BufferedImage and ImageIO

Events

The listeners and events of java.awt.event and javax.swing.event, key bindings, focus traversal and java.beans property change support

Threading

SwingUtilities, EventQueue, SwingWorker and javax.swing.Timer

Services

Toolkit, Desktop (browse, mail and open), the text clipboard, Cursor, UIManager, dragging and dropping, and Font.createFont for a shipped font

SwingX

AreaClasses

Components

JXTable, JXTreeTable, JXList, JXTree, JXDatePicker, JXMonthView, JXTaskPane, JXCollapsiblePane, JXTitledPanel, JXHeader, JXStatusBar, JXHyperlink, JXBusyLabel, JXSearchField, JXErrorPane and the JX versions of the basic components

Rendering

Highlighters, the renderer providers, StringValue and IconValue, and painters

Other

HorizontalLayout and VerticalLayout, sort controllers, RowFilters, TableColumnExt, PromptSupport and AbstractBean

JavaFX

AreaClasses

Application and windows

Application, Platform, HostServices, Stage, Screen and FileChooser

Layout panes

Pane, HBox, VBox, BorderPane, StackPane, GridPane, FlowPane, TilePane, AnchorPane and Region

Controls

Label, the buttons, TextField, TextArea, ComboBox, ChoiceBox, DatePicker, ColorPicker, Spinner, Slider, ProgressBar, ScrollPane, SplitPane, TabPane, TitledPane, Accordion, ToolBar, Pagination, ListView, TableView, TreeView and Tooltip

Charts

LineChart, AreaChart, BarChart, ScatterChart and PieChart

Menus and dialogs

MenuBar, ContextMenu, Alert, Dialog, ChoiceDialog and TextInputDialog

Properties and collections

javafx.beans.property, Bindings, ObservableList, FXCollections, FilteredList and SortedList

Graphics

Shapes, paths, SVGPath, Text, TextFlow, gradients, DropShadow and InnerShadow, transforms, Canvas, Image, WritableImage and Node.snapshot

Animation

Timeline, AnimationTimer and the transitions

Concurrency

Task, Service and Worker

FXML and styling

FXMLLoader, @FXML, style classes, inline styles, PseudoClass and style sheets

Calling Codename One from desktop code

The desktop sources compile in the common module, next to the Codename One API, so they can call it directly. That’s how a ported application adapts to the device it runs on, and how it reaches the camera, the database, push notifications and the rest of Motivation:

if (!CN.isDesktop() && !CN.isTablet()) {
    // A phone: stack the two halves instead of placing them side by side.
    split.setOrientation(JSplitPane.VERTICAL_SPLIT);
}

Code that uses Codename One classes no longer compiles in the original desktop project. Keep such calls behind a small class of your own if the application must go on building for a desktop JVM too.

Mixing with Codename One code

A Codename One application can use Swing and JavaFX screens without handing them the whole application. Put the desktop sources in common/src/main/desktop as usual, and have the main class show Codename One forms instead of calling the desktop application’s main.

SwingInterop.asComponent wraps a Swing component in a Codename One component that you add to a form like any other:

JPanel report = new JPanel(new BorderLayout());
report.add(new JLabel("Orders this week"), BorderLayout.NORTH);
report.add(new JScrollPane(new JTable(new Object[][] {{"Mon", 12}, {"Tue", 9}},
        new Object[] {"Day", "Orders"})), BorderLayout.CENTER);

Form form = new Form("Report", new com.codename1.ui.layouts.BorderLayout());
form.add(com.codename1.ui.layouts.BorderLayout.CENTER, SwingInterop.asComponent(report));
form.show();

FxInterop.asComponent does the same for the root of a JavaFX scene graph, or for a Scene when the screen needs style sheets:

VBox root = new VBox(8, new Label("Orders this week"), new Button("Refresh"));
Scene scene = new Scene(root);
scene.getStylesheets().add("/com/example/orders.css");

Form form = new Form("Report", new BorderLayout());
form.add(BorderLayout.CENTER, FxInterop.asComponent(scene));
form.show();

Both take an Object and throw an IllegalArgumentException for anything other than a Swing component, a JavaFX parent or a scene. Call them on the EDT.

Frames, stages and modal dialogs work from Codename One code too. A frame or a stage opens as a form over the current one, and closing it returns there:

JFrame frame = new JFrame("Edit order");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setContentPane(editor);
frame.setVisible(true);   // closing it returns to the form that was showing

int answer = JOptionPane.showConfirmDialog(frame, "Save the order?", "Save",
        JOptionPane.YES_NO_OPTION);
Stage stage = new Stage();
stage.setTitle("Edit order");
stage.setScene(new Scene(editor));
stage.show();   // closing it returns to the form that was showing

On the desktop ports these windows are real windows. SwingInterop.setNativeWindows(false) and FxInterop.setNativeWindows(false) keep them inside the application’s own window instead, as on a phone.

Testing a ported application

The application’s logic is tested as any Codename One application’s is, with mvn cn1:test or ./gradlew cn1Test. Test classes aren’t relinked, so a unit test exercises the classes that don’t touch the toolkit: models, parsers and the services behind the interfaces of Porting a large application. The tests that came with the desktop project stay there and keep running on the JVM.

Then run the application in the simulator with a phone, a tablet and the desktop skin. Most of what a port gets wrong is visible there in a minute: a window that pans because it can’t shrink, a table whose columns no longer fit, a feature reachable only from a hover or a key binding.

To prove that the screens respond to a real pointer, the Codename One source tree carries a harness in maven/integration-tests/desktop-compat-realport. It starts a built application on the JavaSE port and plays a script of clicks, drags and keys with the display server’s own input, and it fails when anything throws or when a step changes nothing on the screen. A script has one step on a line, and a position is a fraction of the application’s area:

# Wait for the first screen, then keep a picture of it.
sleep 4000
shot start
click 0.061 0.268
type bc
key TAB
drag 0.536 0.573 0.640 0.573 400
# Moving the pointer isn't expected to change anything.
quiet move 0.5 0.5

Run it against the common module of a built application, under xvfb-run on a machine you’re working on:

# From a checkout of the Codename One sources, against a built application:
xvfb-run -a maven/integration-tests/desktop-compat-realport/run.sh \
    /path/to/MyApp/common /path/to/orders.txt /path/to/output

swing-gallery.txt and javafx-gallery.txt beside the harness are complete scripts to start from.

Native desktop builds

The same project builds a native executable for Linux, macOS, and Windows. The application, the part of the layers it uses and the part of Codename One it uses are translated to C and compiled into one program with no Java runtime beside it:

# Linux: a native executable, built on a Linux machine
mvn package -Dcodename1.platform=linux -Dcodename1.buildTarget=local-linux-device

# macOS: a native .app, built on a Mac with Xcode
mvn package -Dcodename1.platform=ios -Dcodename1.buildTarget=local-mac-device

# Windows: a native .exe, built on Windows or cross-compiled
mvn package -Dcodename1.platform=win -Dcodename1.buildTarget=local-windows-device

Each port has a chapter with its toolchain, build hints, signing and packaging: Building a native Linux app, Native macOS Build (AppKit) and Building a native Windows app. A ported application needs nothing beyond what those chapters ask of any application. On these ports a JMenuBar is drawn in its frame, the mouse, wheel and keyboard modifiers are reported, and every further JFrame and Stage is a real window (see Desktop Windows).

Size, memory and start-up

This section measures what the user downloads, how soon the window appears and how much memory the application holds, for the same sources built two ways:

On a JVMNative build

Sources

The application’s own, unmodified

The same, imported

Runtime

A jlink image of exactly the modules jdeps names, stripped and compressed

None

Toolkit

The JDK’s Swing, or OpenJFX

Codename One’s Swing or JavaFX layer over the native port

The JVM runs in two configurations. Default is the image as built, with its class data sharing archive. Tuned is what a developer who cared about start-up and footprint would ship: the serial collector, a smaller thread stack, the first compiler tier only, an application class data sharing archive and the smallest heap the application runs in.

Eight applications were measured: the swing-gallery and javafx-gallery samples in the Codename One source tree, the JDK’s SwingSet2 and Notepad demos, and four small open-source applications. Every comparison is like for like. Both builds of an application ran on the same machine, and each size compares one architecture with the same architecture: on macOS the native application is measured with the arm64 slice of its executable only, the architecture of the jlink runtime it’s compared with. In each ratio the JVM’s figure is divided by the native build’s, so a figure above 1 favors the native build.

Size is bytes on disk, and bytes as one zip at the highest deflate level, which is close to what a download costs. Start-up is the time from the start of the process to the first painted frame, the median of seven launches. Memory is summed over the process tree, ten seconds after the first frame and again after a short script of input. CPU while idle is the processor time of an application waiting for input.

Bar chart of the installed size of each application on Linux: the native build beside the jlink image
Bar chart of the zipped size of each application on Linux: the native build beside the jlink image
Table 19. Linux: the distributed application (MB)
ApplicationToolkitjlink imagejlink image, zippedjpackage image, zippedNative buildNative build, zippedJVM image / native buildJVM zip / native zip

swing-gallery sample

Swing

56.3

34.6

34.8

19.6

7.2

2.9x

4.8x

SwingSet2, the JDK demo

Swing

55.6

34.1

34.2

20.5

8.2

2.7x

4.2x

Notepad, the JDK demo

Swing

54.3

32.8

32.9

17.7

6.5

3.1x

5.0x

Falling-blocks game

Swing

54.1

32.7

32.8

17.4

6.4

3.1x

5.1x

javafx-gallery sample

JavaFX

61.6

39.1

39.2

19.4

7.1

3.2x

5.5x

Open-source 2048 game

JavaFX

61.6

39.1

39.3

18.3

6.7

3.4x

5.8x

Address book

JavaFX

61.5

39.1

39.2

17.8

6.5

3.5x

6.0x

Mandelbrot explorer

JavaFX

61.3

38.9

39.0

17.7

6.5

3.5x

6.0x

The jlink image is the application with the smallest Java runtime that runs it: the application’s jars and the modules jlink found it to need. The native build is one executable file with nothing beside it. Both rely on the desktop libraries of the system, X11 and, for the native build and for JavaFX, GTK; neither column counts them.

Against the jlink image as installed the native build is smaller in 8 applications, of the 8 measured. Its best result is 3.5x for Mandelbrot explorer (17.7 MB against 61.3 MB).

Against the zipped jlink image the native build is smaller in 8 applications, of the 8 measured. Its best result is 6.0x for Mandelbrot explorer (6.5 MB against 38.9 MB).

Bar chart of the installed size of each application on macOS: the native application for arm64 beside the arm64 jlink image
Table 20. macOS: the distributed application (MB)
Applicationjlink image, arm64jlink image, zippedNative .app, arm64Native .app, zippedUniversal .appJVM image / native .appJVM zip / native zip

swing-gallery sample

51.4

32.5

25.7

8.7

49.6

2.0x

3.7x

SwingSet2, the JDK demo

50.7

32.0

26.4

9.8

49.5

1.9x

3.3x

Notepad, the JDK demo

49.4

30.7

23.4

8.0

44.9

2.1x

3.9x

Falling-blocks game

49.3

30.6

23.0

7.8

44.2

2.1x

3.9x

javafx-gallery sample

57.1

37.1

25.5

8.6

49.2

2.2x

4.3x

Open-source 2048 game

57.1

37.1

24.0

8.1

46.0

2.4x

4.6x

Address book

57.0

37.0

23.4

7.9

45.0

2.4x

4.7x

Mandelbrot explorer

56.8

36.9

23.2

7.9

44.6

2.5x

4.7x

Both sides are built for arm64: the jlink image holds the arm64 runtime, and the native .app is the bundle with the arm64 slice of its executable only. The builder’s default output is universal, with an x86_64 slice beside it; its size is in the last size column.

Against the jlink image as installed the native build is smaller in 8 applications, of the 8 measured. Its best result is 2.5x for Mandelbrot explorer (23.2 MB against 56.8 MB).

Against the zipped jlink image the native build is smaller in 8 applications, of the 8 measured. Its best result is 4.7x for Mandelbrot explorer (7.9 MB against 36.9 MB).

Table 21. iOS and Android: the distributed application (MB)
ApplicationiOS .appiOS .app, zippediOS executable, strippedAndroid APKAPK, unpackedOf which dex

swing-gallery sample

28.1

9.4

18.2

3.0

5.8

3.9

SwingSet2, the JDK demo

28.7

10.5

17.6

4.4

7.2

3.8

Notepad, the JDK demo

25.6

8.7

16.3

2.9

5.5

3.5

Falling-blocks game

25.1

8.6

16.1

2.8

5.3

3.4

javafx-gallery sample

27.8

9.3

18.0

3.1

5.9

4.0

Open-source 2048 game

26.1

8.9

16.7

3.1

6.0

3.8

Address book

25.4

8.6

16.5

3.0

5.8

3.9

Mandelbrot explorer

25.3

8.6

16.3

2.9

5.5

3.6

Table 22. How much of the toolkit layer an application keeps
ApplicationToolkitClasses in the layerClasses in the native buildKept

swing-gallery sample

Swing and AWT

1039

737

71%

SwingSet2, the JDK demo

Swing and AWT

1039

587

56%

Notepad, the JDK demo

Swing and AWT

1039

566

54%

Falling-blocks game

Swing and AWT

1039

538

52%

javafx-gallery sample

JavaFX

901

688

76%

Open-source 2048 game

JavaFX

894

413

46%

Address book

JavaFX

896

461

51%

Mandelbrot explorer

JavaFX

896

442

49%

Table 23. Linux: warm start, from process start to the first painted frame (ms, median with the range)
ApplicationJVM, defaultJVM, tunedNative buildDefault / nativeTuned / native

swing-gallery sample

326 (316-336)

194 (193-195)

96 (40-104)

3.4x

2.0x

SwingSet2, the JDK demo

220 (205-221)

145 (144-157)

56 (54-84)

3.9x

2.6x

Notepad, the JDK demo

175 (170-187)

123 (107-134)

55 (54-77)

3.2x

2.2x

Falling-blocks game

102 (101-119)

70 (69-72)

74 (54-102)

1.4x

0.9x

javafx-gallery sample

603 (589-617)

408 (386-428)

100 (97-110)

6.0x

4.1x

Open-source 2048 game

307 (271-326)

190 (160-205)

77 (44-100)

4.0x

2.5x

Address book

407 (389-452)

238 (233-266)

74 (56-88)

5.5x

3.2x

Mandelbrot explorer

392 (373-424)

214 (202-231)

86 (76-94)

4.6x

2.5x

Against the default JVM the native build is quicker in 8 applications, of the 8 measured. Its best result is 6.0x for javafx-gallery sample (100 ms against 603 ms).

Against the tuned JVM the native build is quicker in 7 applications and slower in 1 application, of the 8 measured. Its best result is 4.1x for javafx-gallery sample (100 ms against 408 ms). Its worst is 0.9x for Falling-blocks game (74 ms against 70 ms).

Bar chart of the cold start of each application on Linux: the native build beside the default and the tuned JVM
Table 24. Linux: cold start, from process start to the first painted frame (ms, median with the range)
ApplicationJVM, defaultJVM, tunedNative buildDefault / nativeTuned / native

swing-gallery sample

364 (358-382)

216 (212-236)

108 (97-117)

3.4x

2.0x

SwingSet2, the JDK demo

245 (229-255)

168 (151-180)

90 (86-95)

2.7x

1.9x

Notepad, the JDK demo

199 (195-210)

137 (126-150)

90 (63-92)

2.2x

1.5x

Falling-blocks game

125 (124-140)

93 (91-99)

86 (65-90)

1.5x

1.1x

javafx-gallery sample

637 (631-654)

422 (415-432)

116 (109-120)

5.5x

3.6x

Open-source 2048 game

343 (308-355)

221 (206-226)

109 (107-128)

3.2x

2.0x

Address book

437 (428-477)

262 (258-269)

83 (63-88)

5.3x

3.2x

Mandelbrot explorer

423 (363-428)

238 (196-240)

92 (92-96)

4.6x

2.6x

Against the default JVM the native build is quicker in 8 applications, of the 8 measured. Its best result is 5.5x for javafx-gallery sample (116 ms against 637 ms).

Against the tuned JVM the native build is quicker in 8 applications, of the 8 measured. Its best result is 3.6x for javafx-gallery sample (116 ms against 422 ms).

Bar chart of the resident memory of each idle application on Linux: the native build beside the default and the tuned JVM
Table 25. Linux: memory 10 seconds after the first frame (MB, RSS / PSS)
ApplicationJVM, defaultJVM, tunedNative buildDefault RSS / nativeTuned RSS / native

swing-gallery sample

124.6 / 119.1

84.2 / 78.6

64.5 / 56.4

1.9x

1.3x

SwingSet2, the JDK demo

130.1 / 126.0

86.9 / 82.8

89.8 / 81.3

1.4x

1.0x

Notepad, the JDK demo

84.2 / 80.2

62.2 / 58.2

54.5 / 47.0

1.5x

1.1x

Falling-blocks game

65.7 / 61.8

52.6 / 48.6

52.3 / 45.4

1.3x

1.0x

javafx-gallery sample

285.9 / 258.1

212.2 / 184.5

91.7 / 83.3

3.1x

2.3x

Open-source 2048 game

197.5 / 171.7

152.4 / 126.6

58.9 / 51.8

3.4x

2.6x

Address book

233.6 / 207.4

171.5 / 145.2

55.6 / 48.3

4.2x

3.1x

Mandelbrot explorer

263.0 / 236.0

184.6 / 157.6

120.1 / 112.3

2.2x

1.5x

Against the default JVM the native build is smaller in 8 applications, of the 8 measured. Its best result is 4.2x for Address book (55.6 MB against 233.6 MB).

Against the tuned JVM the native build is smaller in 6 applications and within 5% of it in 2 applications, of the 8 measured. Its best result is 3.1x for Address book (55.6 MB against 171.5 MB).

Table 26. Linux: memory after the scripted interaction (MB, RSS / PSS)
ApplicationJVM, defaultJVM, tunedNative buildDefault RSS / nativeTuned RSS / native

swing-gallery sample

118.4 / 112.9

86.0 / 80.5

90.8 / 81.8

1.3x

0.9x

SwingSet2, the JDK demo

130.8 / 126.8

87.0 / 83.0

92.8 / 84.3

1.4x

0.9x

Notepad, the JDK demo

91.0 / 87.0

63.5 / 59.4

58.2 / 50.6

1.6x

1.1x

Falling-blocks game

68.3 / 64.4

53.8 / 49.9

54.3 / 47.4

1.3x

1.0x

javafx-gallery sample

293.8 / 266.0

213.3 / 185.5

109.4 / 99.7

2.7x

1.9x

Open-source 2048 game

208.3 / 182.4

155.7 / 129.8

100.0 / 93.0

2.1x

1.6x

Address book

245.8 / 219.5

172.6 / 146.3

57.2 / 49.8

4.3x

3.0x

Mandelbrot explorer

293.4 / 266.3

197.6 / 170.6

172.2 / 164.5

1.7x

1.1x

Against the default JVM the native build is smaller in 8 applications, of the 8 measured. Its best result is 4.3x for Address book (57.2 MB against 245.8 MB).

Against the tuned JVM the native build is smaller in 5 applications, larger in 2 applications and within 5% of it in 1 application, of the 8 measured. Its best result is 3.0x for Address book (57.2 MB against 172.6 MB). Its worst is 0.9x for SwingSet2, the JDK demo (92.8 MB against 87.0 MB).

Table 27. Linux: peak RSS over a whole session (MB)
ApplicationJVM, defaultJVM, tunedNative buildDefault / nativeTuned / nativeTuned -Xmx (MB)

swing-gallery sample

128.5

88.0

90.1

1.4x

1.0x

8

SwingSet2, the JDK demo

130.9

87.2

92.5

1.4x

0.9x

24

Notepad, the JDK demo

92.9

64.0

57.7

1.6x

1.1x

8

Falling-blocks game

70.3

54.2

53.9

1.3x

1.0x

8

javafx-gallery sample

296.7

215.8

108.8

2.7x

2.0x

48

Open-source 2048 game

211.5

158.6

125.5

1.7x

1.3x

8

Address book

248.6

175.3

56.7

4.4x

3.1x

48

Mandelbrot explorer

296.1

200.2

340.5

0.9x

0.6x

48

Table 28. Linux: CPU while idle (share of one core)
ApplicationJVM, defaultJVM, tunedNative build

swing-gallery sample

0.0%

0.0%

0.2%

SwingSet2, the JDK demo

0.2%

0.0%

1.6%

Notepad, the JDK demo

0.2%

0.0%

0.0%

Falling-blocks game

1.0%

0.8%

0.6%

javafx-gallery sample

25.4%

27.1%

4.7%

Open-source 2048 game

3.9%

2.5%

4.8%

Address book

0.6%

0.4%

0.0%

Mandelbrot explorer

2.2%

1.8%

0.2%

Of the 8 native builds, 5 idle at or under 1% of a core. Above it: SwingSet2, the JDK demo (1.6%), javafx-gallery sample (4.7%), Open-source 2048 game (4.8%).

Where the figures were measured
  • Linux: Ubuntu 24.04.5 LTS, aarch64, 4 CPUs, 7.7 GB of memory, in a container, hosted on Apple M4 Max. The display is a virtual framebuffer, Xvfb, of 1280 by 800 pixels. GTK 3.24.41. Each display has had one GTK program on it before the measured application starts, as a desktop session has: the first GTK program on a display loads a GL driver to choose its visuals, and no later one does. OpenJFX 23.0.2 for the JavaFX baselines.

  • Tuned JVM flags: -XX:+UseSerialGC -Xss256k -XX:TieredStopAtLevel=1 -XX:SharedArchiveFile=app.jsa, and the -Xmx of the table above.

  • macOS, iOS and Android sizes: macOS 26.6.2, Apple M4 Max, Xcode 27.0.

The Linux display is a software framebuffer for both builds, and start-up and memory were measured on Linux only. Figures move with what an application does at start-up, so measure your own.

Measuring your own application

The harness that produced the tables is in the Codename One source tree, under scripts/desktop-compat-benchmarks, and takes any Swing or JavaFX project. It builds the application both ways, measures both and writes one result.json, which report.py turns into tables:

scripts/desktop-compat-benchmarks/bench.sh --app /path/to/MyDesktopApp --name myapp \
    --kind swing --main com.example.Main --work /path/to/work
scripts/desktop-compat-benchmarks/report.py /path/to/work/myapp/result.json

The README.md in that directory lists what it needs. The phone sizes come from a second script, run against the project the first one imported:

scripts/desktop-compat-benchmarks/mobile-size.sh --target ios \
    --project /path/to/work/myapp/cn1/app --work /path/to/work/myapp-ios
scripts/desktop-compat-benchmarks/mobile-size.sh --target android \
    --project /path/to/work/myapp/cn1/app --work /path/to/work/myapp-android

Troubleshooting

Each entry is a symptom and what to do about it.

Building

SymptomWhat to do

The build names API the Swing or JavaFX layer doesn’t support.

Replace the call with one the layer has, or move the feature behind an interface (Porting a large application). The full list is in common/target/codenameone/compliance_check.txt.

The message points at a library, not at your code.

If the application doesn’t need that part of the library, exclude the library and replace what you used from it.

The build reports java.awt or javax.swing references and suggests enabling the layer.

The sources that use the toolkit are outside src/main/desktop. Move them to common/src/main/desktop/java.

The import stops because it can’t choose an entry point.

Pass -Dcn1.desktop.mainClass to the import goal.

src/main/desktop exists but a runtime dependency is missing.

The common/pom.xml predates the desktop layers. Run the import goal, which adds the profile.

Kotlin sources under src/main/desktop/kotlin aren’t compiled.

Create common/src/main/kotlin, whose presence switches on the Kotlin build. The import goal does this for you.

Two resources have the same name after packaging.

Rename one of them.

A style sheet warning says a property or selector is ignored.

Style that detail in the application’s theme.css, or remove the rule.

JDK API

SymptomWhat to do

java.lang.reflect, Class.getMethod or a similar call is a build error.

Replace reflection with code: PropertyBusinessObject for bean properties, @Mapped for JSON, an interface for plugins.

Code that loads classes by name with Class.forName fails on Android or in the browser.

Those builds are obfuscated and keep only the classes something references, so a name in a string finds nothing. Reference the class directly, or register it in code.

ObjectOutputStream or ObjectInputStream is a build error.

Use Externalizable with Storage, or @Mapped JSON.

HttpURLConnection, java.net.http or sockets are a build error.

Use Rest, ConnectionRequest or com.codename1.io.URL.

Starting a process throws UnsupportedOperationException on a phone.

Processes run on the desktop targets. Check ProcessBuilder.isSupported() and offer the feature where it answers true (Desktop and Backend Processes).

java.math, javax.crypto, java.sql or java.util.zip.ZipFile is a build error.

Use the equivalents in Porting a large application. The zip and GZIP streams work unchanged.

A relative path or a relative file: URL finds nothing.

A device has no working directory. Relative paths resolve inside the application’s storage; ship read-only files as resources.

System.getenv answers null.

A device has no environment. Use display properties or build hints.

Pattern.compile throws for an advanced pattern.

Unicode script and block classes beyond the common ones and unbounded look-behind aren’t supported. Simplify the pattern.

A Locale used as a map key isn’t found.

Key the map by the locale’s string form.

Running

SymptomWhat to do

A method the application overrides is never called.

The layer’s class doesn’t call it, and the build can’t see an override. Find another hook the layer does call, such as a renderer or a listener.

A keyboard shortcut does nothing.

Function keys and Home, End, Page Up, Page Down and Insert aren’t reported, and a phone reports no modifiers. Give the action a menu item or a button too.

The window pans sideways on a phone.

Something in it has a minimum width larger than the display. Use a layout that wraps or stacks, put the wide part in a scroll pane, or lower desktopcompat.scale.

Text is wider or narrower than on the desktop.

Fonts are the platform’s and measure differently. Let a layout manager size the component.

A JavaFX blur or glow isn’t drawn.

Use DropShadow or InnerShadow, which are.

A JavaFX combo box can’t be typed into.

ComboBox and ChoiceBox open the platform’s picker. Use a TextField where the user types.

An image from a relative file: URL is missing.

Load it as a resource. Image.isError() says whether it was found.