<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shai Almog</title>
    <description>The latest articles on DEV Community by Shai Almog (@codenameone).</description>
    <link>https://dev.to/codenameone</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F417973%2Fa7b22dd9-5565-48f5-bfab-7e5035b3888f.png</url>
      <title>DEV Community: Shai Almog</title>
      <link>https://dev.to/codenameone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codenameone"/>
    <language>en</language>
    <item>
      <title>Parley: A Private Messenger Template You Can Brand</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 01 Nov 2026 20:51:05 +0000</pubDate>
      <link>https://dev.to/codenameone/parley-a-private-messenger-template-you-can-brand-9pi</link>
      <guid>https://dev.to/codenameone/parley-a-private-messenger-template-you-can-brand-9pi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fntfy5s1yavarfvvulti0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fntfy5s1yavarfvvulti0.jpg" alt="Parley, an end-to-end encrypted messenger built with Codename One" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plenty of organizations want their own messenger and can't use WhatsApp for it. A hospital that needs staff chat it controls. A company that wants messaging inside its product, under its brand, with no third party reading along. A community that doesn't want to live on someone else's platform. Building a secure messenger from scratch is a long project: the encryption protocol, multi-device sync, push, calls, media, groups and moderation are each a project of their own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parley&lt;/strong&gt; is that messenger, finished. It's the second of this week's templates, after &lt;a href="https://www.codenameone.com/blog/build-a-multiplayer-strategy-game-in-java/" rel="noopener noreferrer"&gt;Skirmish&lt;/a&gt;, and like the others it comes as one Java project with the app, the server and the admin console. Pick the &lt;strong&gt;messaging&lt;/strong&gt; template in the &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt; and you have it. The source is under &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/parley/LICENSE" rel="noopener noreferrer"&gt;MIT-0&lt;/a&gt;: brand it, sell it and keep your changes private.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/parley-chats.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-chats.png" alt="The Parley chat list"&gt;&lt;/a&gt;Chats, groups and channels.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-direct-chat-received.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-direct-chat-received.png" alt="A one-to-one Parley conversation"&gt;&lt;/a&gt;An encrypted conversation.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-call-connected.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-call-connected.png" alt="A connected Parley video call"&gt;&lt;/a&gt;Voice and video calls.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What's in it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Messaging&lt;/td&gt;
&lt;td&gt;One-to-one chats, groups of up to 256 with invite links, broadcast channels, replies, forwards, edits and delete for everyone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media&lt;/td&gt;
&lt;td&gt;Photos, video and voice notes, encrypted on the device before upload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calls&lt;/td&gt;
&lt;td&gt;Voice and video calls over WebRTC, peer to peer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;Disappearing messages, safety numbers, app lock, screen security, and controls for last seen, profile photo, read receipts and typing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Devices&lt;/td&gt;
&lt;td&gt;Linked devices, including a browser as another device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bridges&lt;/td&gt;
&lt;td&gt;Optional Telegram and WhatsApp bridges, clearly marked in every conversation that uses them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operations&lt;/td&gt;
&lt;td&gt;An admin console with a dashboard, users, reports and bridges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reach&lt;/td&gt;
&lt;td&gt;iOS, Android, desktop and the browser from one project, in five languages including right-to-left Hebrew&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The server can't read your users' messages
&lt;/h2&gt;

&lt;p&gt;Parley uses the same design as Signal: X3DH to agree on keys and the Double Ratchet to rotate them with every message. It uses P-256 ECDH, which every platform's native crypto supports, with HKDF-SHA-256 and AES-256-GCM. Each message has its own key, and a key that leaks doesn't expose earlier messages.&lt;/p&gt;

&lt;p&gt;The server stores and forwards envelopes: sender, target device, header, and ciphertext. It never sees the payload, so a breach of your server is a breach of ciphertext. Attachments are encrypted on the device with a fresh key before upload. On the phone, the history is sealed with a key held in the platform keystore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBIGFzIEFsaWNlJ3MgcGhvbmUKICAgIHBhcnRpY2lwYW50IFMgYXMgUGFybGV5IHNlcnZlcgogICAgcGFydGljaXBhbnQgQiBhcyBCb2IncyBwaG9uZQogICAgQi0-PlM6IFB1Ymxpc2ggaWRlbnRpdHkgYW5kIHByZWtleXMKICAgIEEtPj5TOiBGZXRjaCBCb2IncyBwcmVrZXlzCiAgICBOb3RlIG92ZXIgQTogWDNESDogZm91ciBFQ0RIIHJlc3VsdHMsPGJyLz5IS0RGIGludG8gYSByb290IGtleQogICAgQS0-PlM6IEVudmVsb3BlIChjaXBoZXJ0ZXh0IG9ubHkpCiAgICBTLT4-QjogRW52ZWxvcGUgKGNpcGhlcnRleHQgb25seSkKICAgIE5vdGUgb3ZlciBCOiBTYW1lIFgzREgsIHNhbWUgcm9vdCBrZXksPGJyLz5Eb3VibGUgUmF0Y2hldCBmcm9tIGhlcmUgb24%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBIGFzIEFsaWNlJ3MgcGhvbmUKICAgIHBhcnRpY2lwYW50IFMgYXMgUGFybGV5IHNlcnZlcgogICAgcGFydGljaXBhbnQgQiBhcyBCb2IncyBwaG9uZQogICAgQi0-PlM6IFB1Ymxpc2ggaWRlbnRpdHkgYW5kIHByZWtleXMKICAgIEEtPj5TOiBGZXRjaCBCb2IncyBwcmVrZXlzCiAgICBOb3RlIG92ZXIgQTogWDNESDogZm91ciBFQ0RIIHJlc3VsdHMsPGJyLz5IS0RGIGludG8gYSByb290IGtleQogICAgQS0-PlM6IEVudmVsb3BlIChjaXBoZXJ0ZXh0IG9ubHkpCiAgICBTLT4-QjogRW52ZWxvcGUgKGNpcGhlcnRleHQgb25seSkKICAgIE5vdGUgb3ZlciBCOiBTYW1lIFgzREgsIHNhbWUgcm9vdCBrZXksPGJyLz5Eb3VibGUgUmF0Y2hldCBmcm9tIGhlcmUgb24%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="795" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code that does this is short and readable, because the primitives are part of Codename One. This is the start of a session, from &lt;code&gt;Ratchet.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
KeyPair ephemeral = KeyGenerator.ec(256);&lt;br&gt;
PrivateKey me = Identity.privateKey();&lt;br&gt;
byte[] dh1 = KeyAgreement.ecdh(me, pub(signedKey));&lt;br&gt;
byte[] dh2 = KeyAgreement.ecdh(ephemeral.getPrivateKey(), pub(identity));&lt;br&gt;
byte[] dh3 = KeyAgreement.ecdh(ephemeral.getPrivateKey(), pub(signedKey));&lt;br&gt;
byte[] secret = x3dh(dh1, dh2, dh3,&lt;br&gt;
        oneTime == null ? null : KeyAgreement.ecdh(ephemeral.getPrivateKey(), pub(oneTime)));&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


`KeyAgreement`, `KeyGenerator` and `Hkdf` are new in `com.codename1.security`, added for Parley. They work on every platform, on the backend too, and Thursday's post covers them along with the WebRTC API.

## Calls that never touch your server

Calls use WebRTC with DTLS-SRTP, directly between the two devices. The Parley server rings the other side, relays the offer, answer and ICE candidates over the live WebSocket channel and hands out STUN and TURN credentials. The audio and video never pass through it, so a call costs your server almost nothing and stays private.

## Make it yours

The look starts at the top of `theme.css`:



```css
--brand: #0a7d6b;  /* White on the teal reads at 5:1. */
--brand-contrast: #ffffff;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the brand color and every screen follows, with dark mode variants beside it. The name, icon, languages, push and SMS providers, database and hosting work the same way they do in Wayline. A few directions to take it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An internal company messenger:&lt;/strong&gt; run your own TURN relay for calls (&lt;code&gt;parley.ice.turn&lt;/code&gt;) and leave the bridges out, so nothing leaves your infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messaging inside your product:&lt;/strong&gt; a marketplace that lets buyers and sellers talk, a clinic that talks to patients, a coaching app that talks to clients. The chat screens are ordinary Codename One forms, so they fit into another app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A community app:&lt;/strong&gt; channels for announcements, groups for members and the admin console for moderation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your own crypto choices:&lt;/strong&gt; the curve and the cipher are in one class, &lt;code&gt;Ratchet&lt;/code&gt;, if your compliance team has opinions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/parley-privacy.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-privacy.png" alt="Parley privacy settings"&gt;&lt;/a&gt;Privacy settings.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-settings-dark.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-settings-dark.png" alt="Parley settings in dark mode"&gt;&lt;/a&gt;Dark mode.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-admin-dashboard.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-admin-dashboard.png" alt="The Parley admin dashboard"&gt;&lt;/a&gt;The admin dashboard.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Moderation without reading messages
&lt;/h2&gt;

&lt;p&gt;An encrypted messenger still needs moderation. The admin console only sees what the server sees: accounts, message counts, conversation types and busy hours. When a user reports a conversation, their phone attaches the messages they chose to report, so content reaches an admin only through the people in that conversation. That keeps the privacy promise while still letting you handle abuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The development profile runs with nothing installed. Start the server, open two simulators, sign in as &lt;code&gt;alice@parley.example&lt;/code&gt; and &lt;code&gt;bob@parley.example&lt;/code&gt;, and message yourself. The server can also host the browser build, so a laptop browser becomes a linked device. The &lt;a href="https://www.codenameone.com/parley/" rel="noopener noreferrer"&gt;Parley page&lt;/a&gt; has the overview, and the &lt;a href="https://www.codenameone.com/developer-guide/parley-users-guide/" rel="noopener noreferrer"&gt;user's guide&lt;/a&gt; and &lt;a href="https://www.codenameone.com/developer-guide/parley-implementors-guide/" rel="noopener noreferrer"&gt;implementor's guide&lt;/a&gt; cover the rest.&lt;/p&gt;

&lt;p&gt;Tomorrow: &lt;a href="https://www.codenameone.com/blog/launch-your-own-food-delivery-app/" rel="noopener noreferrer"&gt;Savor: A Food Delivery Template for Restaurants and Local Fleets&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Skirmish: A Multiplayer Strategy Game Template in Java</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:31:24 +0000</pubDate>
      <link>https://dev.to/codenameone/skirmish-a-multiplayer-strategy-game-template-in-java-c5d</link>
      <guid>https://dev.to/codenameone/skirmish-a-multiplayer-strategy-game-template-in-java-c5d</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa73bziv56ddzk5de4zss.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa73bziv56ddzk5de4zss.jpg" alt="Skirmish, a 3D strategy game built with Codename One" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For years I avoided pitching Codename One as a game engine. Unity, Unreal and Godot own that market, and I didn't see a reason for a game developer to pick us. That changed while we were building our app templates. A modern mobile game is mostly app: sign-in, menus, a shop, friends lists, quests, push notifications, analytics and a server. The battle itself is a small part of the code. Those other parts are exactly what Codename One is good at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/rewrite-everything-in-java-not-rust/" rel="noopener noreferrer"&gt;Yesterday's post&lt;/a&gt; argued that complete applications are the new starting point. &lt;strong&gt;Skirmish&lt;/strong&gt; is that idea applied to games. It's a real-time strategy game in the style of Clash Royale: two players face each other across a 3D arena, each drawing troops and spells from a deck, and the first to bring down the other's towers wins. The client, the server and the admin console are one Java project. Watch it first:&lt;/p&gt;



&lt;a href="https://www.codenameone.com/blog/release-2026-10-23/skirmish-walkthrough.mp4" rel="noopener noreferrer"&gt;Download the Skirmish walkthrough video&lt;/a&gt;.


&lt;p&gt;Open the &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;, pick the &lt;strong&gt;Skirmish&lt;/strong&gt; template, set your name and package, and you get the whole game. The source is under &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/skirmish/LICENSE" rel="noopener noreferrer"&gt;MIT-0&lt;/a&gt;. No royalties, no per-install fees, no revenue share and no runtime fee that can show up in your inbox next year. The characters and towers are &lt;a href="https://kaylousberg.com/" rel="noopener noreferrer"&gt;KayKit&lt;/a&gt; models released under CC0.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What's in it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Battle&lt;/td&gt;
&lt;td&gt;A 3D arena with animated glTF characters, drag-and-drop cards, elixir, overtime, particle effects and a finishing camera sweep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Online play&lt;/td&gt;
&lt;td&gt;Trophy-banded matchmaking, challenge links, spectating and replays you can seek through&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Progression&lt;/td&gt;
&lt;td&gt;A guided training battle, levels, daily quests, chests and achievements, all granted by the server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monetization&lt;/td&gt;
&lt;td&gt;Gem packs and a season pass with server-side receipt checks, rewarded and interstitial ads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Growth&lt;/td&gt;
&lt;td&gt;Invite-a-friend links, campaign links, push notifications, a home screen spotlight and opt-in funnel analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operations&lt;/td&gt;
&lt;td&gt;An admin console with live matches, player lookup, reports, queries, engagement pipelines and audited moderation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content&lt;/td&gt;
&lt;td&gt;The Forge, an in-game editor where players and designers build cards, arenas, missions and campaigns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-home.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fskirmish-home.png" alt="The Skirmish home screen with the battle button, chests and quests"&gt;&lt;/a&gt;Home: battle, chests and quests.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-battle.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fskirmish-battle.png" alt="A battle in the 3D arena with a hand of cards"&gt;&lt;/a&gt;A battle in the 3D arena.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-finale.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fskirmish-finale.png" alt="The finishing camera sweep over a fallen tower"&gt;&lt;/a&gt;The finishing camera.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The menus are the easy part
&lt;/h2&gt;

&lt;p&gt;Ask anyone who has shipped a mobile game in a game engine where the time went, and menus come up fast. Scroll views that fight the touch input, text that doesn't wrap, layouts that break on a tablet, a store screen built out of sprites. Game engines are built around a scene graph, and a settings screen doesn't fit in one.&lt;/p&gt;

&lt;p&gt;Every button, list, tab and dialog in Skirmish is an ordinary Codename One component, laid out with layout managers and styled in CSS. The wooden buttons you saw in the video are a style, with no images involved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;WoodButton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;--wood-light&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;--wood&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.6mm&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;--wood-dark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l"&gt;--ink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"native:MainBold"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.5mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.2mm&lt;/span&gt; &lt;span class="m"&gt;2mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the look is a gradient and a border, it stays sharp at every screen density. Want a different visual identity? The palette is a list of CSS variables at the top of &lt;code&gt;theme.css&lt;/code&gt; (&lt;code&gt;--night&lt;/code&gt;, &lt;code&gt;--stone&lt;/code&gt;, &lt;code&gt;--wood&lt;/code&gt;, &lt;code&gt;--gold&lt;/code&gt; and more). Change them and every screen follows. Text wraps, lists scroll natively, right-to-left languages work, and the same screens run on a phone, a tablet and the desktop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The battle is 3D, and it's Java
&lt;/h2&gt;

&lt;p&gt;The arena is a &lt;code&gt;GameView&lt;/code&gt; from &lt;code&gt;com.codename1.gaming&lt;/code&gt;, rendered with the GPU API, with a perspective camera behind your king. The characters are rigged glTF models with skeletal animation, a new part of the gaming API that came out of this work. Playing a walk cycle and blending into an attack takes a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
GltfScene knight = GltfLoader.loadScene(in);&lt;br&gt;
unit = new AnimatedModel(device, knight);&lt;br&gt;
unit.hideNode("Shield_Spike");          // the file carries every prop&lt;br&gt;
unit.play("Walking_A", true);&lt;br&gt;
unit.addTo(this);&lt;br&gt;
// in update(double dt): unit.update((float) dt);&lt;br&gt;
// on attack:            unit.crossFade("1H_Melee_Attack_Chop", 0.15f, false);&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Swap in any rigged glTF character with the same animation names and it fights.

## Multiplayer without a networking team

The game rules live in their own module, `rules`. It's deterministic Java with no floating point and no clock, and four things compile it: the app, the server, the replays and the tests. Adding a card is one line:



```java {class="cn1-no-try"}
add(new CardDef("squire", "Squire", CardDef.TROOP, 3)
        .hp(1450).damage(160).hitTicks(24).range(700).speed(45).radius(500));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Online matches run in lockstep over a WebSocket. The server is the referee: it stamps each card a player drops with a tick slightly in the future and sends it to both phones, so both simulations stay identical and nobody can cheat by editing their client. Everything else, from sign-in to the shop, is REST through &lt;code&gt;@RestClient&lt;/code&gt; interfaces in a module both sides share. Client and server compile against the same types, so a renamed field is a compile error on both ends instead of a bug report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBWyJQbGF5ZXIgQSdzIHBob25lPGJyLz5ydWxlcyBtb2R1bGUiXSAtLSAicGxheSBjYXJkIiAtLT4gU1siU2VydmVyIE1hdGNoUm9vbTxici8-cnVsZXMgbW9kdWxlIl0KICAgIEJbIlBsYXllciBCJ3MgcGhvbmU8YnIvPnJ1bGVzIG1vZHVsZSJdIC0tICJwbGF5IGNhcmQiIC0tPiBTCiAgICBTIC0tICJjYXJkIGF0IHRpY2sgTiIgLS0-IEEKICAgIFMgLS0gImNhcmQgYXQgdGljayBOIiAtLT4gQgogICAgUyAtLT4gUlsiUmVwbGF5PGJyLz5ydWxlcyBtb2R1bGUiXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBWyJQbGF5ZXIgQSdzIHBob25lPGJyLz5ydWxlcyBtb2R1bGUiXSAtLSAicGxheSBjYXJkIiAtLT4gU1siU2VydmVyIE1hdGNoUm9vbTxici8-cnVsZXMgbW9kdWxlIl0KICAgIEJbIlBsYXllciBCJ3MgcGhvbmU8YnIvPnJ1bGVzIG1vZHVsZSJdIC0tICJwbGF5IGNhcmQiIC0tPiBTCiAgICBTIC0tICJjYXJkIGF0IHRpY2sgTiIgLS0-IEEKICAgIFMgLS0gImNhcmQgYXQgdGljayBOIiAtLT4gQgogICAgUyAtLT4gUlsiUmVwbGF5PGJyLz5ydWxlcyBtb2R1bGUiXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="861" height="222"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Promotion and monetization are built in
&lt;/h2&gt;

&lt;p&gt;A game that nobody installs doesn't make money. Skirmish uses the Codename One APIs for growth, so the code is short. Inviting a friend from &lt;code&gt;Friends.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
Invite invite = Invites.create(InviteRequest.create()&lt;br&gt;
        .campaign(CAMPAIGN)&lt;br&gt;
        .channel("share_sheet")&lt;br&gt;
        .payload(mine.code())&lt;br&gt;
        .title("Join me in Skirmish")&lt;br&gt;
        .description("A real-time card battle. Build a deck and fight me.")&lt;br&gt;
        .build());&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The friend who joins gets bonus coins, and the Friends screen shows who joined and who has played. The same template wires up:

- **In-app purchases** of gem packs and a season pass, with receipts verified by your server.
- **Rewarded and interstitial ads**, where a rewarded ad grants its reward through the server.
- **Push notifications** when a chest opens or a friend challenges you.
- **Funnel analytics** that show where new players drop off, with opt-in consent.
- **Campaign links** for ads and social posts outside the app, and a home screen spotlight you change from the console.

The implementor's guide includes a step-by-step tutorial on growing a game with these: turn on measurement, read the funnel, bring players back with push, then grow through invitations.

## Run the game like a live service

The admin console ships in the same app. Moderators see live matches, automatic and reported flags, and player history, and they can warn, mute, suspend or ban with an audit trail. Product owners write queries over the game data, set up engagement pipelines and broadcast promotions. It runs on the desktop as well as on a phone.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-overview.png"&amp;gt;&amp;lt;img width="800" height="480" src="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-overview.png" alt="The Skirmish admin console overview on the desktop" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The admin overview.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-pipelines.png"&amp;gt;&amp;lt;img width="800" height="480" src="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-pipelines.png" alt="Engagement pipelines in the Skirmish admin console" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Engagement pipelines.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;


## Make it yours

Skirmish is meant to be remixed. A few directions it could take:

- **Re-skin it** for a different setting: space, pirates, a cartoon farm. Swap the CSS palette, the glTF models and the card portraits. `PortraitStudio` renders new portraits from the models so the art stays consistent.
- **Change the game.** New cards, new arenas and a different elixir economy are data in the `rules` module. Bump `RulesVersion.ID` and the golden replay tests tell you which recorded battles changed.
- **Let players make content.** The Forge lets players build and share packs of cards, arenas and missions, and lets your designers publish official campaigns from the same editor.
- **Point an AI agent at it.** The whole game is one readable Java project, so "turn this into a cyberpunk tower defense with four lanes" is a reasonable prompt to start from.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-deck.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-deck.png" alt="The Skirmish deck builder" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The deck builder.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-shop.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-shop.png" alt="The Skirmish shop with gem packs and a season pass" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The shop.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-friends.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-friends.png" alt="The friends screen with invitations" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Friends and invitations.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;


The development profile needs nothing installed: an in-memory database, demo accounts and a simulated store. Run the server, start the simulator and play against the bot or a second simulator. The [Skirmish page](https://www.codenameone.com/skirmish/) has the overview and the [implementor's guide](https://www.codenameone.com/developer-guide/skirmish-implementors-guide/) walks through every part.

Tomorrow: [Parley: A Private Messenger Template You Can Brand](https://www.codenameone.com/blog/launch-your-own-private-messenger/).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Rewrite Everything in Java: Not Rust</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 30 Oct 2026 22:46:41 +0000</pubDate>
      <link>https://dev.to/codenameone/rewrite-everything-in-java-not-rust-5co8</link>
      <guid>https://dev.to/codenameone/rewrite-everything-in-java-not-rust-5co8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxm2nh4fpm3927rwn714e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxm2nh4fpm3927rwn714e.jpg" alt="A Java word processor running on an iPhone and a Mac" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rust is a great language for a kernel, a driver or a database engine. Code like that can't pause for a garbage collector and has to account for every byte, and Rust's ownership model pays for itself there. Somewhere along the way "rewrite it in Rust" turned into the default answer for everything, including desktop and mobile applications. For applications, I think that's the wrong trade. You pay more to write the code, more to review it and more to build it, and you don't get a better application in return.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So we tested it instead of arguing about it. &lt;a href="https://github.com/storytold/wordcraft" rel="noopener noreferrer"&gt;Wordcraft&lt;/a&gt; is an open source word processor written in Rust with egui. It's a serious project with over 15,000 GitHub stars, a ribbon, styles, tables, track changes and mail merge, and it publishes a 502-row feature catalog, which makes it a fair target. We built &lt;a href="https://github.com/shai-almog/cn1-wordprocessor" rel="noopener noreferrer"&gt;the same word processor in Java with Codename One&lt;/a&gt;, implemented every catalog row Wordcraft implements, and measured both apps on the same GitHub runners in the same CI run.&lt;/p&gt;

&lt;p&gt;The Java version is faster, smaller, uses less memory, has about two thirds of the code and runs its tests several times cheaper. It also feels more native: it has a real macOS menu bar, copies formatted text into other apps, and lets you drag text and files between the document and the rest of the OS. And it ships to iPhone and Android from the same source. The details are below.&lt;/p&gt;

&lt;p&gt;This is also the start of a busy week. A word processor makes the case with one app; the rest of the week is about what you can start with today. Three new full-stack templates join &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;Wayline&lt;/a&gt;: a real-time 3D strategy game, an end-to-end encrypted messenger and a food delivery service. Each one is an app and its server in one Java project that you can brand and ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming This Week
&lt;/h2&gt;

&lt;p&gt;Today is the Java against Rust story. Then one template a day, followed by the framework features those templates pushed into Codename One:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saturday, October 24:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/build-a-multiplayer-strategy-game-in-java/" rel="noopener noreferrer"&gt;Skirmish: A Multiplayer Strategy Game Template in Java&lt;/a&gt;. A real-time strategy game in the style of Clash Royale, with a 3D arena, online matches, a level editor, in-app purchases, ads and a moderation console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunday, October 25:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/launch-your-own-private-messenger/" rel="noopener noreferrer"&gt;Parley: A Private Messenger Template You Can Brand&lt;/a&gt;. An end-to-end encrypted messenger with groups, channels, voice notes, voice and video calls, linked devices and an admin console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday, October 26:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/launch-your-own-food-delivery-app/" rel="noopener noreferrer"&gt;Savor: A Food Delivery Template for Restaurants and Local Fleets&lt;/a&gt;. Customer, restaurant, courier and admin roles in one app, with card holds, live tracking and payouts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday, October 27:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/bring-your-java-code-everywhere/" rel="noopener noreferrer"&gt;More of the Java API, and an Experiment With Unity Games&lt;/a&gt;. A much bigger Java class library, an experiment that runs simple Unity 2D games as Codename One apps, a faster XML parser and runnable samples in the docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wednesday, October 28:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/desktop-dialogs-menus-3d-models/" rel="noopener noreferrer"&gt;Native Menus, File Dialogs and 3D Models&lt;/a&gt;. Native open and save dialogs, native menu bars with standard commands, and animated 3D models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thursday, October 29:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/private-calls-and-safer-backends/" rel="noopener noreferrer"&gt;WebRTC Calls, Key Agreement and ORM Checks at Build Time&lt;/a&gt;. WebRTC calls, key agreement and HKDF everywhere, a backend client with mutual TLS, and ORM mistakes that fail the build.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/blog/build-a-multiplayer-strategy-game-in-java/" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fskirmish-battle.png" alt="A Skirmish battle with towers, troops and a hand of cards"&gt;&lt;/a&gt;Skirmish, Saturday.&lt;br&gt;
&lt;a href="https://www.codenameone.com/blog/launch-your-own-private-messenger/" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-chats.png" alt="The Parley chat list"&gt;&lt;/a&gt;Parley, Sunday.&lt;br&gt;
&lt;a href="https://www.codenameone.com/blog/launch-your-own-food-delivery-app/" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fsavor-customer-home.png" alt="The Savor home screen with food categories and restaurants"&gt;&lt;/a&gt;Savor, Monday.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;Every figure below comes from one CI run (&lt;a href="https://github.com/shai-almog/cn1-wordprocessor/actions/runs/39825464193" rel="noopener noreferrer"&gt;39825464193&lt;/a&gt;) that builds both apps from source and runs them on the same machines against the same documents. The workflow is in the repository, so you can rerun it. Desktop values are macOS / Linux / Windows medians with a 1,000-paragraph document.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What we measured&lt;/th&gt;
&lt;th&gt;Java (Codename One)&lt;/th&gt;
&lt;th&gt;Rust (Wordcraft)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Startup to the first painted document&lt;/td&gt;
&lt;td&gt;370 / 231 / 288 ms&lt;/td&gt;
&lt;td&gt;821 / 246 / 455 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type a character to painted frame&lt;/td&gt;
&lt;td&gt;8.9 / 10.7 / 12.4 ms&lt;/td&gt;
&lt;td&gt;51.9 / 14.9 / 24.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle RAM&lt;/td&gt;
&lt;td&gt;112 / 111 / 90 MiB&lt;/td&gt;
&lt;td&gt;145 / 187 / 106 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Executable size&lt;/td&gt;
&lt;td&gt;34.7 / 37.7 / 32.5 MiB&lt;/td&gt;
&lt;td&gt;55.0 / 75.2 / 57.5 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature catalog rows&lt;/td&gt;
&lt;td&gt;487 of 487 implementable&lt;/td&gt;
&lt;td&gt;487 implemented, 15 stubs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application code&lt;/td&gt;
&lt;td&gt;76,275 lines&lt;/td&gt;
&lt;td&gt;118,659 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokens an AI agent reads to see all of it&lt;/td&gt;
&lt;td&gt;1,006,395&lt;/td&gt;
&lt;td&gt;1,646,847&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold build and full test run&lt;/td&gt;
&lt;td&gt;97 s&lt;/td&gt;
&lt;td&gt;961 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI time per test&lt;/td&gt;
&lt;td&gt;86 ms&lt;/td&gt;
&lt;td&gt;500 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platforms from one source&lt;/td&gt;
&lt;td&gt;macOS, Linux, Windows, iPhone/iPad, Android, browser&lt;/td&gt;
&lt;td&gt;macOS, Linux, Windows, FreeBSD, browser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Java wins 38 of the 42 desktop measurements. The chart shows the gap on macOS as a ratio, so 2× means the Rust version needs twice as much of that resource:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvi73694n7uglhy0uanfy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvi73694n7uglhy0uanfy.jpg" alt="Bar chart of Wordcraft (Rust) divided by the Codename One (Java) version on macOS. Undo 5.9x, Type a character 5.8x, CI time per test 5.8x, Reopen a DOCX 4.2x, Startup 2.2x, Paste 1.7x, Tokens 1.6x, Executable size 1.6x, Save a DOCX 1.6x, Lines of code 1.6x, Idle RAM 1.3x" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other four are close calls, and none of them is a win on every OS:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measurement&lt;/th&gt;
&lt;th&gt;Where Rust was ahead&lt;/th&gt;
&lt;th&gt;Where Java was ahead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Startup with a one-paragraph document&lt;/td&gt;
&lt;td&gt;Linux, by 4 ms (166 against 170 ms)&lt;/td&gt;
&lt;td&gt;macOS (320 against 651 ms) and Windows (189 against 343 ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save a DOCX&lt;/td&gt;
&lt;td&gt;Linux, by 1.8 ms (5.9 against 7.7 ms)&lt;/td&gt;
&lt;td&gt;macOS (7.7 against 12.0 ms) and Windows (7.4 against 10.3 ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak RAM during the workload&lt;/td&gt;
&lt;td&gt;macOS by 7 MiB, Windows by 18 MiB&lt;/td&gt;
&lt;td&gt;Linux (190 against 241 MiB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A 4 ms difference in launching a nearly empty document isn't something a person can notice, and the same measurement on the other two systems goes to Java by hundreds of milliseconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's faster
&lt;/h3&gt;

&lt;p&gt;Typing latency is the number users feel. A character typed into the Java app reaches the screen in 8.9 ms on macOS; the Rust app takes 51.9 ms. At 60 Hz a frame lasts 16.7 ms, so one app stays inside a single frame and the other spends more than three. Undo is the same story, 8.9 ms against 52.3 ms.&lt;/p&gt;

&lt;p&gt;Opening documents shows it too. Reopening the test DOCX takes 31.6 ms on macOS against Wordcraft's 132 ms, and 31.5 ms against 261 ms on Windows.&lt;/p&gt;

&lt;p&gt;How does Java beat Rust here? Codename One compiles the app ahead of time through ParparVM, which translates the bytecode to C after dropping every class and method the app never reaches. The C compiler then inlines and optimizes across what's left. There's no JVM at run time. Garbage collection makes allocation cheap and reclaims memory on its own thread, so the application code has no ownership bookkeeping, no defensive clones and no reference counting. The UI runs on one thread while work such as saving runs in the background, and saving compresses large parts on several cores at once, without lifetimes or &lt;code&gt;Send&lt;/code&gt; bounds to design around. We didn't use profile-guided optimization or special tuning for this benchmark. These are the defaults every Codename One app gets.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's smaller, and it scales better
&lt;/h3&gt;

&lt;p&gt;"Garbage collection wastes memory" is the first thing people tell me when I say Java should be used for desktop and mobile apps. The measurements say otherwise. The executable is less than two thirds the size on every OS, half on Linux. Idle memory is lower on all three, and the gap grows with the document:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Idle RAM&lt;/th&gt;
&lt;th&gt;1 paragraph&lt;/th&gt;
&lt;th&gt;1,000 paragraphs&lt;/th&gt;
&lt;th&gt;10,000 paragraphs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS, Java / Rust&lt;/td&gt;
&lt;td&gt;96 / 106 MiB&lt;/td&gt;
&lt;td&gt;112 / 145 MiB&lt;/td&gt;
&lt;td&gt;220 / 364 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux, Java / Rust&lt;/td&gt;
&lt;td&gt;94 / 164 MiB&lt;/td&gt;
&lt;td&gt;111 / 187 MiB&lt;/td&gt;
&lt;td&gt;198 / 387 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows, Java / Rust&lt;/td&gt;
&lt;td&gt;71 / 82 MiB&lt;/td&gt;
&lt;td&gt;90 / 106 MiB&lt;/td&gt;
&lt;td&gt;169 / 308 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With a 10,000-paragraph document, the Rust version needs between 65% and 95% more memory. Startup with that document is 917 ms against 1,670 ms on macOS. Manual memory management doesn't make an app lean by itself. Data structures do, and a garbage collector frees you to pick good ones instead of the ones the borrow checker accepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  It costs less for AI and CI
&lt;/h3&gt;

&lt;p&gt;The Java code base is about two thirds of the lines and three fifths of the tokens. If you work with coding agents, tokens are the cost model: every task starts with the agent reading code, and every edit it proposes is code a human reviews. A smaller code base for the same feature catalog means less to read, a smaller context and smaller diffs.&lt;/p&gt;

&lt;p&gt;The word processor itself was written that way. I started with Codex, then switched to Claude, and Codename One's built-in MCP server let the agents drive the running app and inspect it while they worked.&lt;/p&gt;

&lt;p&gt;CI tells the same story. A cold build plus the full test suite takes 97 seconds for the Java app and 961 seconds for Wordcraft, on the same runner. The Java tests run on the JVM against the same code that ships natively, so a test run doesn't need a native toolchain. Rust compiles its whole dependency tree and its test binaries before it runs anything. The suites have different sizes (1,123 tests against 1,922), so per test it's 86 ms against 500 ms. About a quarter of Wordcraft's tests cover plumbing such as scroll viewports, popup placement and window state that Codename One provides as framework services and tests once.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's easier to review
&lt;/h3&gt;

&lt;p&gt;Here is how the spell checker loads its 2 MB word list without slowing startup, from &lt;code&gt;Proofing.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
public static synchronized void preload(final Runnable done) {&lt;br&gt;
    if (preloading) return;&lt;br&gt;
    preloading = true;&lt;br&gt;
    new Thread(&lt;br&gt;
            (Runnable) () -&amp;gt; {&lt;br&gt;
              WordList.get();&lt;br&gt;
              extra();&lt;br&gt;
              done.run();&lt;br&gt;
            },&lt;br&gt;
            "proofing-load")&lt;br&gt;
        .start();&lt;br&gt;
}&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Any Java developer can review that in seconds. A background thread fills a shared structure, and the garbage collector owns the memory. There are no lifetimes, no `Arc&amp;lt;Mutex&amp;lt;...&amp;gt;&amp;gt;` and no `Send` bounds.

Familiarity matters too. Far more developers read Java every day than Rust, so the person reviewing a change is more likely to know the idioms. Java also asks less of them. A reviewer checks what the code does, not whether ownership, borrowing, lifetimes and trait bounds were satisfied in a way that happens to compile. That lighter mental load is a real cost difference. When an agent writes 500 lines for you, how fast a human can review them decides how fast you move.

### It's more portable

The same source builds a native iPhone and iPad app, an Android app, a browser app and desktop apps for macOS, Linux, and Windows. CI builds every one of them on each run, with a screenshot from each. On a phone, the ribbon collapses into a tab picker, the page reflows to the screen width and a touch toolbar replaces the status bar. Wordcraft has no mobile target.

There's one platform Wordcraft supports that we don't: FreeBSD. Codename One doesn't target it today. If there's demand for FreeBSD support, we'll add it, so tell us in the comments.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-iphone.jpg"&amp;gt;&amp;lt;img width="552" height="1200" src="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-iphone.jpg" alt="The Java word processor on an iPhone" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;iPhone&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-android.jpg"&amp;gt;&amp;lt;img width="540" height="1200" src="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-android.jpg" alt="The Java word processor on Android" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Android&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;



&amp;lt;figure style="margin:24px 0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-web.jpg"&amp;gt;&amp;lt;img src="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-web.jpg" alt="The Java word processor running in a browser" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The same app in a browser.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;


### It feels more native

This is the part I care about most, because users feel it before they ever see a benchmark. Portability usually means the lowest common denominator. Here it's the other way around: the Java app integrates with the operating system more deeply than the Rust one. On a Mac it has a real menu bar with the standard shortcuts, copies formatted text that Pages and Mail paste correctly, and lets you drag a selection out into another app or drop files and text in from the Finder:

| Feature | Java (Codename One) | Rust (Wordcraft) |
| --- | --- | --- |
| macOS menu bar | A native menu bar with about 40 commands and standard shortcuts | No menu bar, commands live only in the ribbon |
| Copy to other apps | HTML, RTF, Markdown and plain text, so Pages, Mail or a browser receive formatted text | Plain text only |
| Drag out to other apps | A selection drags out as rich text | No drag source |
| Drag in | Files and text, dropped at the caret | Files only |
| Paste in | HTML, RTF, Markdown, images and files | HTML, RTF and images |
| High contrast | Follows the OS setting live | Not supported |
| iPhone, iPad and Android | Native apps with touch selection handles | No mobile target |


&amp;lt;figure style="margin:24px 0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/native-menu-submenu.jpg"&amp;gt;&amp;lt;img width="1400" height="871" src="https://www.codenameone.com/blog/release-2026-10-23/native-menu-submenu.jpg" alt="The Java word processor's native File menu with an Export submenu on macOS" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The native menu bar on macOS, with submenus.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;



&amp;lt;figure style="margin:24px 0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/native-menu-app-roles.jpg"&amp;gt;&amp;lt;img width="1000" height="600" src="https://www.codenameone.com/blog/release-2026-10-23/native-menu-app-roles.jpg" alt="The macOS application menu with About, Settings and Quit" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;About, Settings and Quit where a Mac user expects them. Wednesday's post shows how any app gets this.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;


### It looks at least as good

Wordcraft is on the left, the Java app on the right. Both are polished desktop apps, and both follow the OS dark mode live. Only one of them also looks right on a phone and follows the high contrast setting.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/wordcraft-rust.jpg"&amp;gt;&amp;lt;img width="1600" height="1000" src="https://www.codenameone.com/blog/release-2026-10-23/wordcraft-rust.jpg" alt="Wordcraft, the Rust word processor, showing its built-in sample document" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Wordcraft (Rust). Screenshot from its README.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-editor-light.jpg"&amp;gt;&amp;lt;img width="1600" height="835" src="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-editor-light.jpg" alt="The Codename One word processor in light mode on macOS" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The Java version on macOS.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;



&amp;lt;figure style="margin:24px 0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-editor-dark.jpg"&amp;gt;&amp;lt;img src="https://www.codenameone.com/blog/release-2026-10-23/wordprocessor-editor-dark.jpg" alt="The Codename One word processor in dark mode" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Dark mode, following the OS live.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;


### It's just as secure

Memory safety is the reason most people give for choosing Rust, and it's a good reason. Java has been memory safe since 1995. Bounds are checked, there are no dangling pointers and there's no manual `free`. To its credit, Wordcraft forbids `unsafe` in its own crates, so on memory safety the two apps are even.

The difference is in the supply chain. Wordcraft's `Cargo.lock` pulls in 534 external crates. The Java app depends on the Codename One framework and its own modules. Every dependency is code someone has to trust, update and audit, so a smaller dependency tree is a smaller attack surface.

### Can Rust catch up?

For size and speed, probably. It would take a lot of work and a deep rethink of memory layout and inlining, which means even more code and even slower builds. Matching Java here would mean rebuilding, inside Rust, what Java already provides. That's the cost I'm talking about.

A personal note. I use Firefox every day, and I love what the Rust work did for its speed and stability. But Firefox is a special case, and I think it proves my point. The parts Rust replaced are the low-level ones: the style engine, the renderer, media parsing. That's exactly where Rust belongs, and it works great there. The browser's user interface, the high-level part, is written in JavaScript, a JIT compiled, garbage collected language that gives the team flexibility. Firefox effectively reinvented the high-level side of Java for itself. A browser has to have both halves. Your application only has the second one.

## Why this matters now

When I wrote [Create an Uber Clone in 7 Days](https://www.amazon.com/Create-Uber-Clone-Days-mobile/dp/1983258784) in 2018, building an Uber-style app was the goal. Today it should be where you start. With coding agents, one developer can take on a scope that used to need a team. Hello world isn't a label on a screen anymore. It's a full application with a backend, an admin console, payments and push notifications, and your work begins from there.

At that scale, the language's overhead compounds. Every extra line is something an agent writes, a human reviews and CI builds, on every change, for the life of the product. Java was designed for big code bases maintained by many people, and it shows. It has a huge ecosystem, readable code, a fast compiler, fast tests and a garbage collector that takes memory management off your plate. Spending that budget on lifetimes in a settings screen doesn't buy your users anything.

That's the direction Codename One is taking: complete starting points, plus everything around the code that a real product needs.

Each template is a working application and its server in one Java project. The client and server share the API types, so a REST call or a WebSocket message is type checked on both ends. Growth and monetization are built into the framework instead of bolted on: invite-a-friend links, analytics, push notifications, ads and in-app purchases are all Codename One APIs that the templates already use. Commerce is the next piece we're working on. The template source is free under [MIT-0](https://github.com/codenameone/CodenameOne/blob/master/scripts/skirmish/LICENSE), with no royalties and no revenue share.

## More This Week

### A real-time 3D strategy game, royalty free

For years I avoided pitching Codename One for games. That changed while we built these templates: a modern mobile game is mostly app. Sign-in, menus, a shop, friends, quests, push and a server, with the battle as a small part of the code. **Skirmish** is a real-time strategy game in the style of Clash Royale. Two players face each other across a 3D arena of animated glTF characters, each drawing troops and spells from a deck, and try to bring down the other's towers. Matches run online in lockstep over a WebSocket, with replays, progression, gem packs, a season pass, ads, and invites.


&amp;lt;video controls playsinline preload="metadata" poster="https://www.codenameone.com/blog/release-2026-10-23/skirmish-walkthrough-poster.jpg" style="width:100%;max-width:420px;height:auto;display:block;margin:0 auto" aria-label="A walkthrough of Skirmish: sign-in, the home screen, the guided training battle and a battle against a bot"&amp;gt;
&amp;lt;source src="https://www.codenameone.com/blog/release-2026-10-23/skirmish-walkthrough.mp4" type="video/mp4"&amp;gt;
&amp;lt;a href="https://www.codenameone.com/blog/release-2026-10-23/skirmish-walkthrough.mp4"&amp;gt;Download the Skirmish walkthrough video&amp;lt;/a&amp;gt;.
&amp;lt;/video&amp;gt;


Every menu in that video is a regular Codename One component, laid out with layout managers and styled in CSS.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-home.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-home.png" alt="The Skirmish home screen" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Home, chests and quests.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-online-battle.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-online-battle.png" alt="An online Skirmish match" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;An online match.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-shop.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-shop.png" alt="The Skirmish shop" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The shop.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;


The game is only half of what ships. **The Forge** is a level editor inside the game, where players and your designers build units, arenas, missions and whole campaigns and share them as packs. And an admin console runs the game like a live service: live matches, reports and flags, player history with audited moderation, queries over game data, engagement pipelines and promotions. It works on a phone and on the desktop.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-forge-editor.png"&amp;gt;&amp;lt;img width="1125" height="2436" src="https://www.codenameone.com/developer-guide/img/skirmish-forge-editor.png" alt="The Forge level editor in Skirmish" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The Forge, the level editor.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-forge-arena.png"&amp;gt;&amp;lt;img width="1125" height="2436" src="https://www.codenameone.com/developer-guide/img/skirmish-forge-arena.png" alt="Designing an arena in the Forge" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Designing an arena.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-admin-overview.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/skirmish-admin-overview.png" alt="The Skirmish admin console on a phone" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;The admin console.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;



&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,270px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-overview.png"&amp;gt;&amp;lt;img width="800" height="480" src="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-overview.png" alt="The Skirmish admin console on the desktop" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Live operations on the desktop.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-pipelines.png"&amp;gt;&amp;lt;img width="800" height="480" src="https://www.codenameone.com/developer-guide/img/skirmish-desktop-admin-pipelines.png" alt="Engagement pipelines in the Skirmish admin console" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Engagement pipelines.&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;


Adding a unit to the game is one line in the shared rules module:



```java {class="cn1-no-try"}
add(new CardDef("squire", "Squire", CardDef.TROOP, 3)
        .hp(1450).damage(160).hitTicks(24).range(700).speed(45).radius(500));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;No royalties, no per-install fees, no revenue share. Saturday's &lt;a href="https://www.codenameone.com/blog/build-a-multiplayer-strategy-game-in-java/" rel="noopener noreferrer"&gt;Skirmish article&lt;/a&gt; walks through the arena, the multiplayer model and the monetization.&lt;/p&gt;
&lt;h3&gt;
  
  
  A messenger your server can't read
&lt;/h3&gt;

&lt;p&gt;Hospitals, companies and communities want their own messenger and can't use WhatsApp for it. &lt;strong&gt;Parley&lt;/strong&gt; is that messenger: chats, groups, channels, voice notes, voice and video calls and linked devices, encrypted end to end with X3DH and the Double Ratchet, the design Signal uses. The server stores and forwards ciphertext, and calls go peer to peer.&lt;/p&gt;


&lt;a href="https://www.codenameone.com/developer-guide/img/parley-chats.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-chats.png" alt="The Parley chat list"&gt;&lt;/a&gt;Chats, groups and channels.
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-direct-chat-received.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-direct-chat-received.png" alt="An encrypted Parley conversation"&gt;&lt;/a&gt;An encrypted conversation.
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-call-connected.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-call-connected.png" alt="A Parley call"&gt;&lt;/a&gt;Calls, peer to peer.
&lt;a href="https://www.codenameone.com/developer-guide/img/parley-admin-dashboard.png" rel="noopener noreferrer"&gt;&lt;img width="419" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fparley-admin-dashboard.png" alt="The Parley admin dashboard"&gt;&lt;/a&gt;Moderation without reading messages.


&lt;p&gt;The crypto reads like ordinary Java because the primitives are now part of Codename One:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
KeyPair ephemeral = KeyGenerator.ec(256);&lt;br&gt;
PrivateKey me = Identity.privateKey();&lt;br&gt;
byte[] dh1 = KeyAgreement.ecdh(me, pub(signedKey));&lt;br&gt;
byte[] dh2 = KeyAgreement.ecdh(ephemeral.getPrivateKey(), pub(identity));&lt;br&gt;
byte[] dh3 = KeyAgreement.ecdh(ephemeral.getPrivateKey(), pub(signedKey));&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Sunday's [Parley article](https://www.codenameone.com/blog/launch-your-own-private-messenger/) covers the protocol, moderation and how to brand it.

### Food delivery without the platform's cut

Delivery platforms often take between 15% and 30% of every order. **Savor** gives a restaurant, a chain or a local startup its own service: one app whose landing screen depends on the role (customer, restaurant, courier or admin), card holds captured on delivery, live tracking on a map with no API key bill, and order and delivery state machines that can't get confused when two people act at once.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/savor-customer-home.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/savor-customer-home.png" alt="The Savor customer home screen" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Customer&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/savor-merchant-queue.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/savor-merchant-queue.png" alt="The restaurant's order queue" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Restaurant&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/savor-courier-offer.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/savor-courier-offer.png" alt="A delivery offer for a courier" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Courier&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/savor-admin-dashboard.png"&amp;gt;&amp;lt;img width="420" height="909" src="https://www.codenameone.com/developer-guide/img/savor-admin-dashboard.png" alt="The Savor admin dashboard" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Admin&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;


Monday's [Savor article](https://www.codenameone.com/blog/launch-your-own-food-delivery-app/) shows the flow and the ways to reshape it, from a single restaurant to groceries or flowers.

### More of Java, and a question about Unity

The class library gained 116 classes and interfaces, including `java.util.function`, streams, `Optional`, `java.time` and readers and writers. Existing Java code ports with fewer changes, and ParparVM and the obfuscator strip whatever your app doesn't call, so the overhead is minor. There's still no reflection and no class loading, on purpose.

We also tried something new to learn from what Unity does right: simple Unity 2D projects, C# scripts and all, now build and run as Codename One apps. Five open source games run unmodified. It's a proof of concept, and you can't take any Unity game and run it.


&amp;lt;div class="release-screenshots" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,270px),1fr));gap:16px;align-items:start;margin:24px 0"&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/unity-interop-fruitopia.png"&amp;gt;&amp;lt;img width="960" height="540" src="https://www.codenameone.com/developer-guide/img/unity-interop-fruitopia.png" alt="Fruitopia, an open source Unity platformer, running on Codename One" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Fruitopia&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;figure style="margin:0"&amp;gt;&amp;lt;a href="https://www.codenameone.com/developer-guide/img/unity-interop-sokoban.png"&amp;gt;&amp;lt;img width="960" height="540" src="https://www.codenameone.com/developer-guide/img/unity-interop-sokoban.png" alt="An open source Unity Sokoban game running on Codename One" loading="lazy" style="width:100%;height:auto" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Sokoban&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;
&amp;lt;/div&amp;gt;


Would that be useful to you? Tuesday's [Java and Unity article](https://www.codenameone.com/blog/bring-your-java-code-everywhere/) asks, and also covers a faster XML parser and a button in the API docs that runs each sample in your browser.

### Desktop apps that feel native

The word processor needed a real menu bar and real file dialogs to hold up next to a native app, so now every Codename One app has them. A `Command` goes into the native menu bar with one call, and standard commands land where each OS expects them, with its own shortcuts:



```java {class="cn1-no-try"}
Command settings = Command.createDesktopRole(Command.DESKTOP_ROLE_SETTINGS, e -&amp;gt; showSettings());
Command quit = Command.createDesktopRole(Command.DESKTOP_ROLE_QUIT, e -&amp;gt; quit());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's "Settings…" with Cmd+, on a Mac, "Options" under Tools on Windows and "Preferences" under Edit on Linux. Open and save dialogs are the operating system's own, with filters, options and a preview where the platform supports them:&lt;/p&gt;


&lt;a href="https://www.codenameone.com/blog/release-2026-10-23/file-dialog-preview-option.jpg" rel="noopener noreferrer"&gt;&lt;img width="800" height="634" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-23%2Ffile-dialog-preview-option.jpg" alt="An open dialog with an image preview and a Remove location data option"&gt;&lt;/a&gt;An open dialog with a preview and an option.
&lt;a href="https://www.codenameone.com/blog/release-2026-10-23/native-menu-app-roles.jpg" rel="noopener noreferrer"&gt;&lt;img width="800" height="480" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-23%2Fnative-menu-app-roles.jpg" alt="The macOS application menu with About, Settings and Quit"&gt;&lt;/a&gt;Standard commands in the macOS application menu.


&lt;p&gt;Wednesday's &lt;a href="https://www.codenameone.com/blog/desktop-dialogs-menus-3d-models/" rel="noopener noreferrer"&gt;desktop article&lt;/a&gt; has the details, plus the animated 3D models behind Skirmish.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calls, crypto and an ORM that fails the build
&lt;/h3&gt;

&lt;p&gt;Voice and video calls are now a Codename One API, built on WebRTC on Android, iOS and the browser. Signaling stays yours, so the same code works with any server:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
WebRtc.openUserMedia(true, true, true).ready(media -&amp;gt; {&lt;br&gt;
    pc = WebRtc.createPeerConnection(config, listener);&lt;br&gt;
    pc.addTrack(media.getAudio());&lt;br&gt;
    pc.addTrack(media.getVideo());&lt;br&gt;
    pc.createOffer().ready(offer -&amp;gt; pc.setLocalDescription(offer)&lt;br&gt;
            .ready(done -&amp;gt; signalling.send(offer)));&lt;br&gt;
});&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Turning a key agreement into an encryption key takes two lines, and works the same on every device and on the server:



```java {class="cn1-no-try"}
byte[] shared = KeyAgreement.ecdh(mine.getPrivateKey(), theirs.getPublicKey());
byte[] key = Hkdf.derive("SHA-256", null, shared, "chat message key v1".getBytes(), 32);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On the server, a partner API that wants a client certificate and HTTP/2 is one builder:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```java {class="cn1-no-try"}&lt;br&gt;
WebClient partner = new WebClient()&lt;br&gt;
        .setClientCertificate(ClientCertificate.pkcs12(archive, password))&lt;br&gt;
        .setRequireHttp2(true)&lt;br&gt;
        .setRequestTimeoutMillis(10000);&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


And with Hibernate and Spring, plenty of mapping mistakes compile fine and blow up when the app starts or on the first request that touches the entity. Codename One's ORM reads your entities at compile time, so these are build errors:



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/temporal"&gt;@temporal&lt;/a&gt; requires a java.util.Date field: signedUp&lt;br&gt;
Persistent fields must be writable: name&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/entity"&gt;@entity&lt;/a&gt; class com.example.Customer must declare a public no-arg constructor&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Thursday's [calls and backends article](https://www.codenameone.com/blog/private-calls-and-safer-backends/) goes through each of these.

## Where is this going?

I don't want this read as "Rust is bad." If you're writing a kernel module, a codec, an embedded controller or anything with hard real-time limits, determinism matters more than throughput, and Rust is an excellent choice. A user-facing application has different constraints. It can trade determinism for throughput, for a smaller code base and for a team that moves faster.

For applications, Java is the better tool, and Codename One is turning into the place where you start with a whole product instead of a blank screen. The word processor is now on the [demos page](https://www.codenameone.com/demos/). Clone the [repository](https://github.com/shai-almog/cn1-wordprocessor), rerun the benchmark workflow and check my numbers. If you find a measurement I got wrong, open an issue there. And if you want to start a project today, the [Initializr](https://www.codenameone.com/initializr/) generates any of the templates in a minute.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>A Java IDE in Your Browser, With Your Files in Your Browser</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Thu, 29 Oct 2026 19:19:54 +0000</pubDate>
      <link>https://dev.to/codenameone/a-java-ide-in-your-browser-with-your-files-in-your-browser-1g9p</link>
      <guid>https://dev.to/codenameone/a-java-ide-in-your-browser-with-your-files-in-your-browser-1g9p</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqoh1wp0idz4dtk6zyf0r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqoh1wp0idz4dtk6zyf0r.jpg" alt="A browser IDE shows Java source beside its running application" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're used to opening a page and immediately changing an app, installing a Java development environment is a substantial detour. I want your first encounter with Codename One to be the app running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our new &lt;a href="https://cloud.codenameone.com/ide/" rel="noopener noreferrer"&gt;browser IDE&lt;/a&gt; is a &lt;strong&gt;highly experimental, very early preview&lt;/strong&gt;. Open it, press Run and start changing the Java. It has a project tree, multiple files and an application preview. Think of the Playground growing into a proper IDE workspace.&lt;/p&gt;

&lt;p&gt;I'm calling it &lt;strong&gt;Not a Cloud IDE&lt;/strong&gt; because the editing, compilation for preview and application execution happen in your browser. Your project files stay in that browser's storage. The ordinary build server enters the workflow when you request a cloud build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with an app on the screen
&lt;/h2&gt;

&lt;p&gt;The first visit opens a GettingStarted project. Press &lt;strong&gt;Run&lt;/strong&gt; in the top toolbar. The editor compiles Java and shows the application beside its source. This screenshot shows that starter running in the public preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-16/browser-ide-preview.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foskwo717a9kuh50glxwy.jpg" alt="GettingStarted.java open beside the running Hi World application in the browser IDE" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can start with a small edit. Inside the starter's &lt;code&gt;runApp()&lt;/code&gt; method, this replaces its initial screen with a counter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First app"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BoxLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;Label&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Trips: 0"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Add a trip"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Trips: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(++&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;])));&lt;/span&gt;
&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The starter already imports the UI and layout classes. Run it, click the button and change the label. The point of this first exercise is to shorten the distance between an idea and a visible result.&lt;/p&gt;

&lt;p&gt;Compiler errors appear against the source. Completion, hover information and source navigation help you explore the API. They don't amount to a full Java language server or IntelliJ's refactoring model, and I don't want to present them that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What runs where
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBFZGl0W0VkaXQgcHJvamVjdCBmaWxlc10gLS0-IFN0b3JlW0Jyb3dzZXIgc3RvcmFnZSBvbiB0aGlzIGRldmljZV0KICAgIFN0b3JlIC0tPiBDb21waWxlW0phdmEgY29tcGlsZXIgaW4gdGhlIGJyb3dzZXJdCiAgICBDb21waWxlIC0tPiBUcmFuc2xhdGVbUGFycGFyVk0gSmF2YVNjcmlwdCB0cmFuc2xhdGlvbl0KICAgIFRyYW5zbGF0ZSAtLT4gUHJldmlld1tBcHBsaWNhdGlvbiBwcmV2aWV3IGluIHRoZSBwYWdlXQogICAgU3RvcmUgLS0-IFpJUFtEb3dubG9hZCBwcm9qZWN0IFpJUF0KICAgIENvbXBpbGUgLS0-IFJlcXVlc3RbRXhwbGljaXQgY2xvdWQgYnVpbGQgcmVxdWVzdF0KICAgIFJlcXVlc3QgLS0-IFNlcnZlcltFeGlzdGluZyBDb2RlbmFtZSBPbmUgYnVpbGQgc2VydmVyXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBFZGl0W0VkaXQgcHJvamVjdCBmaWxlc10gLS0-IFN0b3JlW0Jyb3dzZXIgc3RvcmFnZSBvbiB0aGlzIGRldmljZV0KICAgIFN0b3JlIC0tPiBDb21waWxlW0phdmEgY29tcGlsZXIgaW4gdGhlIGJyb3dzZXJdCiAgICBDb21waWxlIC0tPiBUcmFuc2xhdGVbUGFycGFyVk0gSmF2YVNjcmlwdCB0cmFuc2xhdGlvbl0KICAgIFRyYW5zbGF0ZSAtLT4gUHJldmlld1tBcHBsaWNhdGlvbiBwcmV2aWV3IGluIHRoZSBwYWdlXQogICAgU3RvcmUgLS0-IFpJUFtEb3dubG9hZCBwcm9qZWN0IFpJUF0KICAgIENvbXBpbGUgLS0-IFJlcXVlc3RbRXhwbGljaXQgY2xvdWQgYnVpbGQgcmVxdWVzdF0KICAgIFJlcXVlc3QgLS0-IFNlcnZlcltFeGlzdGluZyBDb2RlbmFtZSBPbmUgYnVpbGQgc2VydmVyXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="694" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The site delivers the editor and toolchain. Dependencies may need downloading too. That doesn't make the editing session a remote machine: there is no server-side workspace running your development loop.&lt;/p&gt;

&lt;p&gt;A cloud build is an explicit handoff of build inputs to the existing Codename One service. It is separate from local preview and doesn't back up your editable workspace. Opening the IDE also doesn't make the whole workflow available offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser storage is local storage
&lt;/h2&gt;

&lt;p&gt;The toolbar says &lt;strong&gt;Saved only in this browser · No cloud backup&lt;/strong&gt;. Take that literally. Signing in on a second computer or switching browser profiles won't bring your project along.&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;Download ZIP&lt;/strong&gt; to keep a backup and take the project to your desktop. Clearing the site's browser data or using private browsing can erase your local work. The export gives you a project you can continue building with the usual Maven or Gradle tools.&lt;/p&gt;

&lt;p&gt;This is also why I like the approach for getting started. You can try the framework in a page, then keep the code when you're ready to set up a local environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the experiment ends today
&lt;/h2&gt;

&lt;p&gt;This will not replace IntelliJ. We can't reproduce the full Java ecosystem, every desktop tool and every build plugin inside JavaScript. Browser preview supports a defined subset of dependency and build behavior; projects outside that subset need a local build.&lt;/p&gt;

&lt;p&gt;Backend projects can be created, edited and exported, but there is no backend runtime in the browser. Full stack preview currently runs the client only. Don't expect &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;Wayline's backend&lt;/a&gt; to start inside the editor tab.&lt;/p&gt;

&lt;p&gt;The IDE exposes settings, GUI Builder and cloud-build actions, but this is early software. A successful browser preview is also not proof that every device API will behave identically on a phone. Check your real targets as the project grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the first experiment small
&lt;/h2&gt;

&lt;p&gt;Last week's &lt;a href="https://www.codenameone.com/blog/playground-real-java-compiler/" rel="noopener noreferrer"&gt;Playground compiler&lt;/a&gt; showed real Java compilation in a page. The IDE gives that loop a workspace you can grow beyond one snippet.&lt;/p&gt;

&lt;p&gt;Try the counter, change its layout and download the project. That is the experience I want to make ordinary: a person curious about Java can see their own change running before spending the afternoon configuring tools.&lt;/p&gt;

&lt;p&gt;This closes our &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;weekly release series&lt;/a&gt;. Between a full-stack ride-hailing application and a browser workspace, we're working on both ends of the same problem: getting you to the part of an application that is yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What is the first small app you would try if opening a page were all the setup it needed?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Let Users Install Your Java Web App Again</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Wed, 28 Oct 2026 21:00:54 +0000</pubDate>
      <link>https://dev.to/codenameone/let-users-install-your-java-web-app-again-4hcm</link>
      <guid>https://dev.to/codenameone/let-users-install-your-java-web-app-again-4hcm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Firw4qsu6mq59r3q02ue2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Firw4qsu6mq59r3q02ue2.jpg" alt="A Java web app moves from a browser tab to an installed app" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We lost progressive web app support when we moved the JavaScript port from TeaVM to ParparVM. It's back. Users can install your browser app on supporting browsers, and its cached application files can load when the network is unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a small business, a link can be the quickest way to put an app in someone's hands. Installation lets that same app remain within reach after the tab closes. This is Wednesday's follow-up to the &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;Wayline release story&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build makes the installation files
&lt;/h2&gt;

&lt;p&gt;A ParparVM JavaScript application build now produces &lt;code&gt;manifest.json&lt;/code&gt;, home-screen icons and &lt;code&gt;sw.js&lt;/code&gt; automatically. There is no separate PWA enablement switch.&lt;/p&gt;

&lt;p&gt;Deploy the whole generated bundle, including subdirectories, over HTTPS. Localhost also works for testing. Relative manifest and asset URLs let an application live under a path such as &lt;code&gt;/dispatch/&lt;/code&gt; rather than requiring a dedicated domain root.&lt;/p&gt;

&lt;p&gt;You can customize the app's identity through build hints in &lt;code&gt;codenameone_settings.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.javascript.manifest.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Station Dispatch&lt;/span&gt;
&lt;span class="py"&gt;codename1.arg.javascript.manifest.short_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Dispatch&lt;/span&gt;
&lt;span class="py"&gt;codename1.arg.javascript.manifest.description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Your station's dispatch app&lt;/span&gt;
&lt;span class="py"&gt;codename1.arg.javascript.manifest.display&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;standalone&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default manifest already requests a standalone window. &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;short_name&lt;/code&gt; default to the application's display name. Installation UI and eligibility depend on the browser, so avoid making your onboarding depend on one particular prompt appearing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline launch has a precise boundary
&lt;/h2&gt;

&lt;p&gt;The service worker caches generated application files: the runtime, translated JavaScript chunks, themes and bundled resources. It must finish installing while online before those files can support an offline launch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBPbmxpbmVbRmlyc3QgdmlzaXQgd2hpbGUgb25saW5lXSAtLT4gSW5zdGFsbFtJbnN0YWxsIHNlcnZpY2Ugd29ya2VyXQogICAgSW5zdGFsbCAtLT4gQ2FjaGVbQ2FjaGUgZ2VuZXJhdGVkIGFwcGxpY2F0aW9uIGZpbGVzXQogICAgQ2FjaGUgLS0-IE9mZmxpbmVbTGF1bmNoIGNhY2hlZCBhcHAgd2l0aG91dCBhIG5ldHdvcmtdCiAgICBPZmZsaW5lIC0tPiBMb2NhbFtVc2UgYXZhaWxhYmxlIGxvY2FsIGRhdGEgYW5kIGZlYXR1cmVzXQogICAgT2ZmbGluZSAtLT4gQVBJW05ldHdvcmsgb3BlcmF0aW9ucyBzdGlsbCBuZWVkIHRoZSBzZXJ2ZXJd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBPbmxpbmVbRmlyc3QgdmlzaXQgd2hpbGUgb25saW5lXSAtLT4gSW5zdGFsbFtJbnN0YWxsIHNlcnZpY2Ugd29ya2VyXQogICAgSW5zdGFsbCAtLT4gQ2FjaGVbQ2FjaGUgZ2VuZXJhdGVkIGFwcGxpY2F0aW9uIGZpbGVzXQogICAgQ2FjaGUgLS0-IE9mZmxpbmVbTGF1bmNoIGNhY2hlZCBhcHAgd2l0aG91dCBhIG5ldHdvcmtdCiAgICBPZmZsaW5lIC0tPiBMb2NhbFtVc2UgYXZhaWxhYmxlIGxvY2FsIGRhdGEgYW5kIGZlYXR1cmVzXQogICAgT2ZmbGluZSAtLT4gQVBJW05ldHdvcmsgb3BlcmF0aW9ucyBzdGlsbCBuZWVkIHRoZSBzZXJ2ZXJd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="586" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn't cache API responses, proxy requests or external resources. Application data and synchronization remain your responsibility.&lt;/p&gt;

&lt;p&gt;In Wayline, that means a cached UI is not permission to accept a ride offline. Matching needs the server, and the map's external tiles and routes aren't bundled application assets. A dispatch app should say that it is disconnected rather than show a stale assignment as confirmed.&lt;/p&gt;

&lt;p&gt;This distinction is useful outside ride hailing too. A locally stored draft can remain editable; publishing that draft still needs a connection. Design the interaction around what you actually have on the device.&lt;/p&gt;

&lt;h2&gt;
  
  
  An update should belong to one version
&lt;/h2&gt;

&lt;p&gt;A running app needs its runtime and resources to agree. The worker creates a new cache when content changes, even if the application's version string stays the same.&lt;/p&gt;

&lt;p&gt;The new worker waits until the old app's tabs and windows close before activating. That keeps an open app from mixing an old runtime with new chunks. Each deployment path has its own cache, so two apps on the same host don't share one undifferentiated bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBPbGQgYXMgT3BlbiBhcHAKICAgIHBhcnRpY2lwYW50IFdvcmtlciBhcyBOZXcgd29ya2VyCiAgICBwYXJ0aWNpcGFudCBDYWNoZSBhcyBOZXcgY2FjaGUKICAgIFdvcmtlci0-PkNhY2hlOiBTdG9yZSBjaGFuZ2VkIGJ1bmRsZQogICAgV29ya2VyLT4-V29ya2VyOiBXYWl0IHdoaWxlIG9sZCBhcHAgcmVtYWlucyBvcGVuCiAgICBPbGQtPj5PbGQ6IFVzZXIgY2xvc2VzIG9sZCB0YWJzIGFuZCB3aW5kb3dzCiAgICBXb3JrZXItPj5Xb3JrZXI6IEFjdGl2YXRlIG5ldyB2ZXJzaW9uCiAgICBXb3JrZXItPj5DYWNoZTogVXNlIG1hdGNoaW5nIGFwcGxpY2F0aW9uIGZpbGVz%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBPbGQgYXMgT3BlbiBhcHAKICAgIHBhcnRpY2lwYW50IFdvcmtlciBhcyBOZXcgd29ya2VyCiAgICBwYXJ0aWNpcGFudCBDYWNoZSBhcyBOZXcgY2FjaGUKICAgIFdvcmtlci0-PkNhY2hlOiBTdG9yZSBjaGFuZ2VkIGJ1bmRsZQogICAgV29ya2VyLT4-V29ya2VyOiBXYWl0IHdoaWxlIG9sZCBhcHAgcmVtYWlucyBvcGVuCiAgICBPbGQtPj5PbGQ6IFVzZXIgY2xvc2VzIG9sZCB0YWJzIGFuZCB3aW5kb3dzCiAgICBXb3JrZXItPj5Xb3JrZXI6IEFjdGl2YXRlIG5ldyB2ZXJzaW9uCiAgICBXb3JrZXItPj5DYWNoZTogVXNlIG1hdGNoaW5nIGFwcGxpY2F0aW9uIGZpbGVz%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="747" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deploy the files together and allow the browser to revalidate &lt;code&gt;sw.js&lt;/code&gt;. Cache versioning helps the client choose a coherent bundle; it doesn't turn a partially uploaded deployment into a complete one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the behavior your users will get
&lt;/h2&gt;

&lt;p&gt;After deploying a test build, open the browser's Application tools and inspect the manifest, service worker and cache. Wait for installation to finish, then reload offline. Check both the UI that should work and the operations that should report a missing connection.&lt;/p&gt;

&lt;p&gt;For an update check, keep one old tab open, deploy a visibly changed build and confirm that the new worker waits. Close the old tabs and reopen the app. Test a subdirectory deployment too if that is how you host it.&lt;/p&gt;

&lt;p&gt;The repository includes a &lt;a href="https://github.com/codenameone/CodenameOne/tree/master/tests/pwa" rel="noopener noreferrer"&gt;browser smoke test and service-worker contract checks&lt;/a&gt;. Those tests exercise caching and update behavior; they don't establish that every browser offers the same install experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/6022" rel="noopener noreferrer"&gt;PR #6022&lt;/a&gt; restores the capability, and the &lt;a href="https://www.codenameone.com/developer-guide/working-with-javascript/" rel="noopener noreferrer"&gt;JavaScript guide&lt;/a&gt; explains manifest customization. A web deployment can be a useful first distribution channel for your application, with native apps alongside it when their device integration matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What should your app still let a user do after the connection drops?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Give Your Desktop App a Useful Title Bar and Real Tools</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 27 Oct 2026 19:01:49 +0000</pubDate>
      <link>https://dev.to/codenameone/give-your-desktop-app-a-useful-title-bar-and-real-tools-199h</link>
      <guid>https://dev.to/codenameone/give-your-desktop-app-a-useful-title-bar-and-real-tools-199h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmo3k6g27l7tzg2ls0pxc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmo3k6g27l7tzg2ls0pxc.jpg" alt="A desktop application combines title controls with process and archive tools" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A document editor needs room for the document. Giving an entire row to a title, then adding another row for Save and Search, spends that room quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can now put Codename One components into the title area of native macOS, Windows and Linux apps. It's a niche capability until you're building an editor. Then the details of window dragging and button hit testing become part of your product.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;weekly follow-up&lt;/a&gt; also covers two less visible desktop capabilities: launching external tools and reading or writing ZIP streams. Together they make it easier to build an application that fits into the user's existing workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-16/native-editor-titlebar.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9vytvg8omo9mlbav23e.jpg" alt="Native macOS editor title area with Save, Undo, document status and command search beside the OS window controls" width="799" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Our native macOS editor prototype, cropped to its title area and ribbon. Save, Undo, document status and command search use the top row; the document begins below the ribbon.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Put Save where the user is already looking
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;desktop.titleBar=custom&lt;/code&gt;, then supply a title component through the ordinary toolbar API. This is the &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/DesktopTitleContent.java" rel="noopener noreferrer"&gt;guide's editor example&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="nf"&gt;createEditor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"desktop.titleBar"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"custom"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Toolbar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setGlobalToolbar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Quarterly report"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="nc"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WEST&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CENTER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Quarterly report"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EAST&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToolbar&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setTitleComponent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call it from the application's normal lifecycle, after framework initialization and before constructing other forms that need this mode. The &lt;code&gt;Command&lt;/code&gt; arguments are your actions. You can also select the mode with &lt;code&gt;codename1.arg.desktop.titleBar=custom&lt;/code&gt; in the build settings.&lt;/p&gt;

&lt;p&gt;Don't add fixed padding for macOS traffic lights or Windows caption buttons. The framework reserves space for the real OS controls, accounting for changes such as window DPI. Your components stay in the Java component tree while the port handles window gestures.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Native desktop&lt;/th&gt;
&lt;th&gt;Who owns the window behavior?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;AppKit keeps the traffic lights, resizing and system menu. Empty title space follows native dragging and the user's double-click preference.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;The Desktop Window Manager keeps caption controls. Native hit testing handles empty title space and resize edges.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;A GTK header bar places rendered application content beside real window controls and follows desktop decoration settings.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Buttons, editable fields and other interactive descendants keep their input. Disabled buttons are excluded from dragging too. Plain labels and passive container backgrounds can move the window. For a custom painted background, &lt;code&gt;Toolbar.setTitleBarDragRegion(component, true)&lt;/code&gt; opts it into dragging while preserving interactive children.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBQb2ludGVyW1BvaW50ZXIgaW4gdGhlIHRpdGxlIGFyZWFdIC0tPiBUYXJnZXR7V2hhdCBpcyB1bmRlciBpdD99CiAgICBUYXJnZXQgLS0-fEFwcGxpY2F0aW9uIGNvbnRyb2x8IEphdmFbRGVsaXZlciBjb21wb25lbnQgaW5wdXRdCiAgICBUYXJnZXQgLS0-fFBhc3NpdmUgdGl0bGUgYmFja2dyb3VuZHwgT1NbTmF0aXZlIHdpbmRvdyBnZXN0dXJlXQogICAgVGFyZ2V0IC0tPnxXaW5kb3cgY29udHJvbCBvciBlZGdlfCBOYXRpdmVbT1MgY29tbWFuZCBvciByZXNpemVd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBQb2ludGVyW1BvaW50ZXIgaW4gdGhlIHRpdGxlIGFyZWFdIC0tPiBUYXJnZXR7V2hhdCBpcyB1bmRlciBpdD99CiAgICBUYXJnZXQgLS0-fEFwcGxpY2F0aW9uIGNvbnRyb2x8IEphdmFbRGVsaXZlciBjb21wb25lbnQgaW5wdXRdCiAgICBUYXJnZXQgLS0-fFBhc3NpdmUgdGl0bGUgYmFja2dyb3VuZHwgT1NbTmF0aXZlIHdpbmRvdyBnZXN0dXJlXQogICAgVGFyZ2V0IC0tPnxXaW5kb3cgY29udHJvbCBvciBlZGdlfCBOYXRpdmVbT1MgY29tbWFuZCBvciByZXNpemVd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="793" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are platform differences. Windows custom mode omits the separate Win32 menu row; provide visible access through your toolbar, side menu or ribbon. Linux keeps its menu row below the header, and macOS keeps the system menu bar. This applies to the main form in native desktop builds. Separate dialogs retain their decorations, and browser pages don't gain control of browser chrome.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/developer-guide/desktop-integration/" rel="noopener noreferrer"&gt;desktop integration guide&lt;/a&gt; has the full contract, introduced in &lt;a href="https://github.com/codenameone/CodenameOne/pull/6019" rel="noopener noreferrer"&gt;PR #6019&lt;/a&gt;. The useful feature is the complete interaction, including the parts that keep a click on Search from moving the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Call the tool your user already has
&lt;/h2&gt;

&lt;p&gt;We originally wanted a Process API for backend work. It made sense for desktop applications too: an editor can invoke a converter, inspect a tool's version or run an existing command-line utility.&lt;/p&gt;

&lt;p&gt;The shared classes are &lt;code&gt;com.codename1.io.ProcessBuilder&lt;/code&gt; and &lt;code&gt;com.codename1.io.Process&lt;/code&gt;. Check support before offering the action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;canRunTools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ProcessBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupported&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API works in JavaSE desktop builds, the native Windows/macOS/Linux ports, the JVM backend and the packaged Linux ParparVM backend. Android, iOS and the browser don't support process launching through this API.&lt;/p&gt;

&lt;p&gt;Here is the launch portion of a worker task, assuming &lt;code&gt;toolPath&lt;/code&gt; names an installed executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Process&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ProcessBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toolPath&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--version"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;redirectErrorStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOutputStream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// This command needs no stdin.&lt;/span&gt;
    &lt;span class="c1"&gt;// Start a reader for child.getInputStream() before waiting.&lt;/span&gt;
    &lt;span class="c1"&gt;// Apply a timeout, inspect the exit code, and handle the output.&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a launch excerpt, not a complete runner. The &lt;a href="https://www.codenameone.com/developer-guide/processes/" rel="noopener noreferrer"&gt;complete guide example&lt;/a&gt; drains output on a separate thread and uses a timed wait. Waiting first can deadlock if the child fills its output pipe. Keep both process I/O and waits off the UI event thread.&lt;/p&gt;

&lt;p&gt;Arguments are passed literally; the API doesn't insert a shell. Working directories are native path strings. File redirection, pipelines and &lt;code&gt;ProcessHandle&lt;/code&gt; aren't part of this subset. Closing the process requests termination and closes its streams, but doesn't kill descendants or wait for exit. &lt;a href="https://github.com/codenameone/CodenameOne/pull/6010" rel="noopener noreferrer"&gt;PR #6010&lt;/a&gt; contains the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give users an archive they can use
&lt;/h2&gt;

&lt;p&gt;ParparVM now implements the streaming subset of &lt;code&gt;java.util.zip&lt;/code&gt;. That includes ZIP entry streams, GZIP, deflate/inflate and checksums. JVM and Android builds use their platform implementations; ParparVM uses the existing Java compression engine.&lt;/p&gt;

&lt;p&gt;Writing a small UTF-8 entry looks familiar. Here &lt;code&gt;destination&lt;/code&gt; is the output stream your caller supplies; closing the ZIP also closes it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZipOutputStream&lt;/span&gt; &lt;span class="n"&gt;zip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZipOutputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putNextEntry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZipEntry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.txt"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Completed rides\n"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closeEntry&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A project export has an extra requirement: its launcher should still be executable after extraction. &lt;code&gt;ZipArchiveOutputStream&lt;/code&gt; adds a permission argument to the familiar entry operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ZipEntry&lt;/span&gt; &lt;span class="n"&gt;launcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ZipEntry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"project/run.sh"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;launcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putNextEntry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;launcher&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mo"&gt;0755&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Copy the launcher bytes to zip, then call closeEntry().&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;zip&lt;/code&gt; is a &lt;code&gt;com.codename1.io.zip.ZipArchiveOutputStream&lt;/code&gt;. The &lt;a href="https://www.codenameone.com/developer-guide/backend-operations/#backend-zip" rel="noopener noreferrer"&gt;server download example&lt;/a&gt; streams directly to an HTTP response and includes the full copy loop. This avoids building an entire archive in memory, although entry metadata still takes space.&lt;/p&gt;

&lt;p&gt;This isn't every Java SE archive feature. ParparVM's subset excludes random-access &lt;code&gt;ZipFile&lt;/code&gt;, ZIP64 and encrypted archives. Reading to the end of an entry verifies its CRC and size; closing an archive early doesn't verify unread entries. An importer still needs to validate paths and bound extracted data. &lt;a href="https://github.com/codenameone/CodenameOne/pull/6006" rel="noopener noreferrer"&gt;PR #6006&lt;/a&gt; introduced the streaming support.&lt;/p&gt;

&lt;p&gt;A useful title area, an external tool and an export users can open are modest features individually. They are also the things that decide whether a desktop app can become part of someone's daily work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What would you put in your app's title area if the window controls were already handled?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Build Your App Theme Without a Display Server</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 26 Oct 2026 19:15:24 +0000</pubDate>
      <link>https://dev.to/codenameone/build-your-app-theme-without-a-display-server-35c3</link>
      <guid>https://dev.to/codenameone/build-your-app-theme-without-a-display-server-35c3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkvjylui4nz99qfmwtsja.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkvjylui4nz99qfmwtsja.jpg" alt="A stylesheet compiles to a theme resource without a display server" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A build container shouldn't need a pretend monitor to compile a stylesheet. Until now, parts of our CSS build path pulled in desktop machinery that made that a practical concern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The CSS compiler now runs &lt;strong&gt;100% headless&lt;/strong&gt;. You can compile the same theme over SSH, in a container or on a CI worker without starting a display server or virtual framebuffer. This is Monday's build-tools installment of the &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;weekly series&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the output, remove the window
&lt;/h2&gt;

&lt;p&gt;Codename One CSS is compiled into a resource file. The app loads &lt;code&gt;theme.res&lt;/code&gt;; it doesn't parse your stylesheet at runtime.&lt;/p&gt;

&lt;p&gt;The compiler needs enough of the framework to construct fonts, convert some units and process images. Previously, reaching those APIs could bring desktop initialization along with them. The new implementation supplies the services theme construction needs without starting a GUI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBDU1NbQ1NTIGFuZCBsb2NhbCBpbXBvcnRzXSAtLT4gQ29tcGlsZXJbSGVhZGxlc3MgQ1NTIGNvbXBpbGVyXQogICAgSW1hZ2VzW0ltYWdlIHJlc291cmNlc10gLS0-IENvbXBpbGVyCiAgICBDb21waWxlciAtLT4gVGhlbWVbdGhlbWUucmVzXQogICAgVGhlbWUgLS0-IEFwcFtBcHBsaWNhdGlvbiBsb2FkcyBpdHMgdGhlbWVd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBDU1NbQ1NTIGFuZCBsb2NhbCBpbXBvcnRzXSAtLT4gQ29tcGlsZXJbSGVhZGxlc3MgQ1NTIGNvbXBpbGVyXQogICAgSW1hZ2VzW0ltYWdlIHJlc291cmNlc10gLS0-IENvbXBpbGVyCiAgICBDb21waWxlciAtLT4gVGhlbWVbdGhlbWUucmVzXQogICAgVGhlbWUgLS0-IEFwcFtBcHBsaWNhdGlvbiBsb2FkcyBpdHMgdGhlbWVd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="995" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Images still need decoding and scaling. The implementation uses Java's image APIs with buffered images, which work in headless mode. A build without a monitor is still a build that processes visual assets.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/maven/css-compiler/src/main/java/com/codename1/designer/css/HeadlessCssCompilerImplementation.java" rel="noopener noreferrer"&gt;headless implementation&lt;/a&gt; fixes the conversion values used during theme construction instead of taking them from the build machine's display. That removes another source of variation between a developer laptop and a worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use your existing CSS workflow
&lt;/h2&gt;

&lt;p&gt;New Maven apps already enable CSS. An older project can enable it in &lt;code&gt;common/codenameone_settings.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.cssTheme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The existing goal still compiles the theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn cn1:css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a check that explicitly exercises headless operation, run the goal with the JVM's headless flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;cn1:css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your normal application build invokes CSS compilation as needed too. The &lt;a href="https://www.codenameone.com/developer-guide/maven-appendix-goals/#_compile_css_css" rel="noopener noreferrer"&gt;CSS goal reference&lt;/a&gt; and &lt;a href="https://www.codenameone.com/developer-guide/css/" rel="noopener noreferrer"&gt;CSS chapter&lt;/a&gt; cover the project layout and output.&lt;/p&gt;

&lt;p&gt;Stylesheets can be split into local files. Imports go before ordinary rules, and a URL inside an imported stylesheet remains relative to that file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"widgets/buttons.css"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;WlPrimary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;#1f5eff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.4mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;WlPrimary&lt;/code&gt; is a component UIID, the name a Codename One component uses to select its style. It isn't a browser class selector. Missing imports and import cycles fail the build. Remote stylesheet imports are refused.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets faster, and what to measure
&lt;/h2&gt;

&lt;p&gt;Removing GUI startup and framebuffer setup removes work from the theme build. It also removes display configuration that an automated tool otherwise has to install and diagnose. That's a useful reduction even before comparing build times.&lt;/p&gt;

&lt;p&gt;I don't have a representative end-to-end timing to attach to every project. A large build may spend most of its time compiling Java or downloading dependencies. Measure the CSS step and the complete build separately before attributing a change to this compiler.&lt;/p&gt;

&lt;p&gt;A simulator still displays a GUI. UI tests still need their own execution environment. This change lets a theme compile without one; it doesn't make every graphical test headless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The risk is in the small details
&lt;/h2&gt;

&lt;p&gt;Our CSS compiler already had fragile corners. Replacing its execution environment is a change I want users to benefit from, and also one I want them to check carefully.&lt;/p&gt;

&lt;p&gt;A build can succeed while a shadow, font metric or image border looks different. Compare representative screens after updating, particularly where styles depend on physical units, generated images, pressed states or dark-mode overrides. A tiny stylesheet that reproduces a difference is much more useful than a screenshot of a whole application without its theme.&lt;/p&gt;

&lt;p&gt;Wayline gives us concrete screens to inspect. Its primary buttons share a style, and the rider map switches colors with the theme:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-rider-home.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-rider-home.png" alt="Wayline rider map in the light theme."&gt;&lt;/a&gt;Wayline rider map in the light theme.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-rider-home-dark.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-rider-home-dark.png" alt="The same rider screen in the dark theme."&gt;&lt;/a&gt;The same rider screen in the dark theme.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;These are maintained Wayline guide screenshots, not a before-and-after comparison of compiler output.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The new compiler is in &lt;a href="https://github.com/codenameone/CodenameOne/pull/5965" rel="noopener noreferrer"&gt;PR #5965&lt;/a&gt;. Try the headless CSS goal on your CI image, then inspect the screens your users actually use. The build setup should get smaller while the app keeps the appearance you chose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which part of your build still needs a desktop environment when it shouldn't?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Thymeleaf, Spring MVC and htmx for Native Java Backends</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 25 Oct 2026 17:22:45 +0000</pubDate>
      <link>https://dev.to/codenameone/thymeleaf-spring-mvc-and-htmx-for-native-java-backends-3kma</link>
      <guid>https://dev.to/codenameone/thymeleaf-spring-mvc-and-htmx-for-native-java-backends-3kma</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzukl1ji4qwoz09j0ogk9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzukl1ji4qwoz09j0ogk9.jpg" alt="Thymeleaf-style templates and Spring MVC controllers in a native Java backend" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thymeleaf, Spring MVC and htmx are a combination Java developers can put to work quickly: templates for the view, controllers for the request and partial updates where they help. We now support that programming model with a different execution path: statically compiled rendering code inside a native Java backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The template compiler processes a supported subset of Thymeleaf syntax and emits Java renderers with direct model access. Spring MVC-style controllers choose the page or fragment. ParparVM compiles the renderers with the rest of the backend into native code. The response is still ordinary HTML that any browser can display.&lt;/p&gt;

&lt;p&gt;This is Sunday's follow-up to &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;Create an Uber Clone in 1 Minute&lt;/a&gt;. It also gives you another way to build an admin site: ordinary HTML served by your Java backend, without requiring a Codename One client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Move template work out of the request path
&lt;/h2&gt;

&lt;p&gt;The performance case is concrete: production doesn't parse template files, interpret property expressions or discover model accessors through reflection. A model property becomes a Java getter or field access; a loop becomes generated control flow. The native compiler sees that code alongside the controller and service.&lt;/p&gt;

&lt;p&gt;With htmx, the server returns only the part of the page that changed. Static rendering and smaller responses address different costs, and work well together. Database work, network latency and the page itself still determine end-to-end response time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the useful part of JSP
&lt;/h2&gt;

&lt;p&gt;JSP compiled pages into Java servlet code, as &lt;a href="https://tomcat.apache.org/tomcat-10.1-doc/jasper-howto.html" rel="noopener noreferrer"&gt;Tomcat's Jasper documentation&lt;/a&gt; describes. Native compilation wasn't the JSP contract. The useful idea was that a template could become code the runtime already knew how to execute.&lt;/p&gt;

&lt;p&gt;The old workflow suited a long-running server you could telnet into and edit in place. I want to edit quickly while developing too. In production, I want a built artifact that went through the same checks as the Java beside it.&lt;/p&gt;

&lt;p&gt;Thymeleaf supports &lt;a href="https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#template-cache" rel="noopener noreferrer"&gt;template caching&lt;/a&gt;, so it would be wrong to describe every request as parsing a fresh file. Our choice goes further: the request path has no template interpreter at all. Editing a template means rebuilding the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBIVE1MW1RlbXBsYXRlcyBhbmQgbW9kZWwgZGVjbGFyYXRpb25zXSAtLT4gQnVpbGRbVGVtcGxhdGUgY29tcGlsZXJdCiAgICBCdWlsZCAtLT4gSmF2YVtKYXZhIHJlbmRlcmVyc10KICAgIEphdmEgLS0-IEpWTVtKVk0gZGV2ZWxvcG1lbnQgc2VydmVyXQogICAgSmF2YSAtLT4gTmF0aXZlW1BhcnBhclZNIG5hdGl2ZSBwYWNrYWdlXQogICAgTmF0aXZlIC0tPiBSZXF1ZXN0W0V4ZWN1dGUgcmVuZGVyZXIgZm9yIGVhY2ggcmVxdWVzdF0%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBIVE1MW1RlbXBsYXRlcyBhbmQgbW9kZWwgZGVjbGFyYXRpb25zXSAtLT4gQnVpbGRbVGVtcGxhdGUgY29tcGlsZXJdCiAgICBCdWlsZCAtLT4gSmF2YVtKYXZhIHJlbmRlcmVyc10KICAgIEphdmEgLS0-IEpWTVtKVk0gZGV2ZWxvcG1lbnQgc2VydmVyXQogICAgSmF2YSAtLT4gTmF0aXZlW1BhcnBhclZNIG5hdGl2ZSBwYWNrYWdlXQogICAgTmF0aXZlIC0tPiBSZXF1ZXN0W0V4ZWN1dGUgcmVuZGVyZXIgZm9yIGVhY2ggcmVxdWVzdF0%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1337" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8tx1xzxnxa5u1zbr256.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8tx1xzxnxa5u1zbr256.jpg" alt="The runnable Java catalog, showing its inventory table and Add product action" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The sample running locally on the JVM with generated view classes. The same renderers enter the native package.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The controller still looks like a controller
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/codenameone/CodenameOne/tree/master/scripts/backend-mvc" rel="noopener noreferrer"&gt;runnable catalog&lt;/a&gt; has an in-memory inventory. Its listing method is short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;populate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Htmx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"products :: catalog"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"products"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method belongs to a &lt;code&gt;@Controller&lt;/code&gt; using &lt;code&gt;com.codename1.backend.annotations&lt;/code&gt; and &lt;code&gt;com.codename1.backend.mvc&lt;/code&gt; imports. &lt;code&gt;populate&lt;/code&gt; copies the catalog into the model. A normal request gets the page; a request from htmx gets its named &lt;code&gt;catalog&lt;/code&gt; fragment.&lt;/p&gt;

&lt;p&gt;That return value names a compiled view. It doesn't open a path supplied by the request. The list is synchronized because this small example keeps mutable data in memory; restarting it resets the catalog. A persistent application would delegate to a service and database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell the compiler what your model contains
&lt;/h2&gt;

&lt;p&gt;The template declares the Java type of &lt;code&gt;products&lt;/code&gt;. A shortened version of the catalog view looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- cn1:model products java.util.List&amp;lt;example.Product&amp;gt; --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"workspace"&lt;/span&gt; &lt;span class="na"&gt;th:fragment=&lt;/span&gt;&lt;span class="s"&gt;"catalog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;th:each=&lt;/span&gt;&lt;span class="s"&gt;"product : ${products}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;th:text=&lt;/span&gt;&lt;span class="s"&gt;"${product.name}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Notebook&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;th:text=&lt;/span&gt;&lt;span class="s"&gt;"${product.quantity}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;12&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;th:text=&lt;/span&gt;&lt;span class="s"&gt;"${product.active ? 'Active' : 'Inactive'}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Active&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler resolves the Java getters or public fields and emits direct access. Rename a property without updating the template and the build fails with a file and source position. That is a useful failure to get before deployment.&lt;/p&gt;

&lt;p&gt;Dynamic text is escaped. URL expressions encode their arguments before HTML attribute escaping. Raw HTML insertion and arbitrary method calls are outside this first version's expression language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update a section without building a second frontend
&lt;/h2&gt;

&lt;p&gt;The Add product link in the catalog works as ordinary navigation and carries the htmx enhancement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://www.codenameone.com/products/new"&lt;/span&gt;
   &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/products/new"&lt;/span&gt;
   &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#workspace"&lt;/span&gt;
   &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add product&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server can return the editor fragment and replace the workspace. The sample includes a pinned local htmx script, shared layout fragments and forms that also work with JavaScript disabled.&lt;/p&gt;

&lt;p&gt;Form binding uses a dedicated DTO. Keep &lt;code&gt;BindingResult&lt;/code&gt; immediately after that parameter, then return the editor when validation fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@ModelAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ProductForm&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;BindingResult&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;HttpServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasErrors&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Add product"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nextId&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;saved&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an excerpt from &lt;code&gt;Catalog&lt;/code&gt;, including its storage and validation helpers by reference. &lt;code&gt;saved&lt;/code&gt; chooses the full-navigation redirect or the updated fragment. Returning form errors with status 200 lets htmx display them normally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4ipzfep2m9lkrm9kh3v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4ipzfep2m9lkrm9kh3v.jpg" alt="An empty product name returns an inline validation error through htmx" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Submitting the form without a name returns the editor fragment with the error beside the field.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The existing security chain handles cross-site request forgery (CSRF) protection. Generated forms and mutating htmx controls carry the available token. Declaring an HTML controller doesn't create that security configuration for you; the sample's &lt;code&gt;WebSecurity&lt;/code&gt; shows it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A smaller language is a trade
&lt;/h2&gt;

&lt;p&gt;This is a Thymeleaf-style subset, not the Thymeleaf library. There is no Spring Expression Language, custom dialect support, arbitrary method invocation or runtime template editing. Form DTOs support scalar values; they don't bind whole ORM entity graphs. The &lt;a href="https://www.codenameone.com/developer-guide/backend-views/" rel="noopener noreferrer"&gt;views chapter&lt;/a&gt; lists supported attributes and migration boundaries.&lt;/p&gt;

&lt;p&gt;Those restrictions buy typed property access and a renderer that ships as ordinary compiled application code. They also mean porting an existing template can take work. I would start with a small admin page and its form errors before attempting a large site.&lt;/p&gt;

&lt;p&gt;From &lt;code&gt;scripts/backend-mvc&lt;/code&gt;, with this checkout's backend and plugins installed, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn package
mvn com.codenameone:codenameone-maven-plugin:8.0-SNAPSHOT:backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080/products&lt;/code&gt;. The sample's README has the native packaging command and a smoke test covering normal forms, htmx, escaping and CSRF rejection. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5974" rel="noopener noreferrer"&gt;PR #5974&lt;/a&gt; introduced the implementation.&lt;/p&gt;

&lt;p&gt;I want the development loop to stay short. I also want the production binary to know how to render every page before the first user arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which template error would you most like your build to catch?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Spring Data-Style Repositories Compiled to Native Code</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 24 Oct 2026 16:40:54 +0000</pubDate>
      <link>https://dev.to/codenameone/spring-data-style-repositories-compiled-to-native-code-2f4b</link>
      <guid>https://dev.to/codenameone/spring-data-style-repositories-compiled-to-native-code-2f4b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9y30kv07ot8sjn54mkkd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9y30kv07ot8sjn54mkkd.jpg" alt="Repository queries and transaction completion in one application" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use Spring Data, a method such as &lt;code&gt;findFirstByDriverAndStateIn&lt;/code&gt; tells you what a repository does before you open its implementation. Codename One now brings that repository model to both the client and backend, with generated Java code that can compile directly into a native executable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The advantage is familiar data access with the runtime setup removed. The build generates concrete repository implementations. Native packaging compiles them ahead of time, so startup doesn't need to scan for repositories or generate proxies. Direct calls also give the compiler code it can analyze and optimize.&lt;/p&gt;

&lt;p&gt;This week's &lt;a href="https://www.codenameone.com/blog/create-an-uber-clone-in-1-minute/" rel="noopener noreferrer"&gt;Wayline application&lt;/a&gt; uses these repositories for its ride-hailing service. Alongside them, Spring-style transaction isolation and completion callbacks let you control when another part of the system can act on a database change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the query where you can find it
&lt;/h2&gt;

&lt;p&gt;Wayline's repository declares what it needs to read. This shortened interface comes from its &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/wayline/backend/src/main/java/com/codenameone/examples/wayline/repository/RideRepository.java" rel="noopener noreferrer"&gt;RideRepository&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.orm.repository.CrudRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.orm.repository.Repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codenameone.examples.wayline.api.RideState&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codenameone.examples.wayline.domain.Ride&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Repository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;RideRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CrudRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="nf"&gt;findFirstByRiderAndStateIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideState&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="nf"&gt;findFirstByDriverAndStateIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideState&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build generates the implementation. It makes direct calls into the session; it doesn't discover repositories by scanning classes at runtime. There is no Hibernate dependency or runtime proxy generation. The same repository API works with client ORM sessions and backend sessions.&lt;/p&gt;

&lt;p&gt;Spring Data users will recognize the naming convention. Hibernate users will recognize managed entities and dirty checking. These are supported conventions under Codename One's packages, with a deliberately bounded API. Existing Spring or Hibernate dependencies don't become portable just because the method names look familiar.&lt;/p&gt;

&lt;p&gt;The inherited methods include &lt;code&gt;insert&lt;/code&gt;, &lt;code&gt;save&lt;/code&gt;, &lt;code&gt;findById&lt;/code&gt;, &lt;code&gt;findAll&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;existsById&lt;/code&gt; and &lt;code&gt;deleteById&lt;/code&gt;. There are a couple of details to remember: a missing &lt;code&gt;findById&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt;, and &lt;code&gt;save&lt;/code&gt; returns the managed object produced by &lt;code&gt;merge&lt;/code&gt;. Keep that returned value if you continue changing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static compilation does the setup work once
&lt;/h2&gt;

&lt;p&gt;A repository interface becomes a concrete implementation during the build. With ParparVM, that Java implementation joins the rest of the application in the native binary. There is no reflective repository dispatch or runtime implementation factory to initialize.&lt;/p&gt;

&lt;p&gt;That removes work from startup and from dispatching repository calls. It doesn't remove the cost of the database query: indexes, round trips and the number of rows still matter. &lt;code&gt;CASE&lt;/code&gt; aggregation and pagination help keep that work in the database instead of loading unnecessary entities.&lt;/p&gt;

&lt;h2&gt;
  
  
  A long method name isn't always the clearest query
&lt;/h2&gt;

&lt;p&gt;Derived finders cover comparisons, &lt;code&gt;And&lt;/code&gt;/&lt;code&gt;Or&lt;/code&gt;, explicit ordering and limited results. A final &lt;code&gt;PageRequest(offset, size)&lt;/code&gt; gives a list query a page without issuing a separate total-count query. Include a unique tie-breaker in the ordering so rows with equal scores don't wander between pages.&lt;/p&gt;

&lt;p&gt;For a conditional aggregate, declare the query. This shortened example uses the guide's &lt;code&gt;RepositoryRecord&lt;/code&gt; entity, which has a numeric &lt;code&gt;score&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"select sum(case when r.score &amp;gt;= :floor then 1 else 0 end) "&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"from com.codenameone.developerguide.snippets.repository.RepositoryRecord r"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="nf"&gt;qualified&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Param&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"floor"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CASE&lt;/code&gt; expressions let the database count qualifying rows instead of making the app fetch them all. Named values are bound parameters. Native SQL queries are available too, with a narrower scalar-result API and the portability costs of the SQL you choose.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/developer-guide/annotation-sqlite-orm/#generated-orm-repositories" rel="noopener noreferrer"&gt;repository reference&lt;/a&gt; spells out the grammar. Unsupported method signatures fail generation. JPQL syntax and mapped names are checked when the method creates its query, before SQL execution. That distinction matters: this isn't a promise that every query error becomes a compile error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the transaction in the service
&lt;/h2&gt;

&lt;p&gt;Repositories don't open or commit transactions. The service owns that boundary. Here is Wayline's actual read path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RideDto&lt;/span&gt; &lt;span class="nf"&gt;activeForRider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rides&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findFirstByRiderAndStateIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;toDto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The snippet sits inside &lt;code&gt;Rides&lt;/code&gt;, with its repository and mapping helpers. Methods that change several entities use a transaction around that unit of work. A repository's session participates in it, so changing a managed entity can be flushed when the transaction completes.&lt;/p&gt;

&lt;p&gt;Bulk operations deserve separate attention. Derived deletes and declared modifying queries flush pending changes, execute bulk SQL and clear the persistence context. They bypass per-entity callbacks and cascades. Reload objects before continuing; references you kept still contain their old Java values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose isolation at the outer boundary
&lt;/h2&gt;

&lt;p&gt;The annotation now accepts an isolation level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.Isolation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.Transactional&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Annotation for a service method that owns the transaction:&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isolation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Isolation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SERIALIZABLE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an annotation excerpt, to place on the method that starts the work. It requests a physical transaction with that level. It doesn't silently upgrade an outer transaction that already exists.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Explicit isolation levels&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SERIALIZABLE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;READ_COMMITTED&lt;/code&gt;, &lt;code&gt;REPEATABLE_READ&lt;/code&gt;, &lt;code&gt;SERIALIZABLE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL/MariaDB&lt;/td&gt;
&lt;td&gt;Those three plus &lt;code&gt;READ_UNCOMMITTED&lt;/code&gt;, with transactional tables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A joined scope requesting an explicit level must match the outer scope's explicit level. Joining a &lt;code&gt;DEFAULT&lt;/code&gt; scope with an explicit request fails. Choose the level at the start, or use &lt;code&gt;REQUIRES_NEW&lt;/code&gt; when the operation really needs an independent transaction and connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notify after the database agrees
&lt;/h2&gt;

&lt;p&gt;Inside a thread-bound transaction, register a &lt;code&gt;Transactions.Synchronization&lt;/code&gt;. This is the guide's callback shape, with comments marking where application work belongs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Transactions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerSynchronization&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transactions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Synchronization&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeCommit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;readOnly&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Validate before the ORM flush and commit.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterCommit&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Invalidate a cache or schedule an event.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterCompletion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Transactions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Outcome&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Release resources for COMMITTED, ROLLED_BACK, or UNKNOWN.&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBTZXJ2aWNlCiAgICBwYXJ0aWNpcGFudCBEQiBhcyBEYXRhYmFzZQogICAgcGFydGljaXBhbnQgSG9vayBhcyBDb21wbGV0aW9uIGNhbGxiYWNrCiAgICBTZXJ2aWNlLT4-REI6IENoYW5nZSByb3dzCiAgICBTZXJ2aWNlLT4-REI6IENvbW1pdAogICAgREItLT4-U2VydmljZTogQ29tbWl0IGFja25vd2xlZGdlZAogICAgU2VydmljZS0-PlNlcnZpY2U6IENsb3NlIHNlc3Npb24gYW5kIHJlbGVhc2UgY29ubmVjdGlvbgogICAgU2VydmljZS0-Pkhvb2s6IGFmdGVyQ29tbWl0CiAgICBTZXJ2aWNlLT4-SG9vazogYWZ0ZXJDb21wbGV0aW9u%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBTZXJ2aWNlCiAgICBwYXJ0aWNpcGFudCBEQiBhcyBEYXRhYmFzZQogICAgcGFydGljaXBhbnQgSG9vayBhcyBDb21wbGV0aW9uIGNhbGxiYWNrCiAgICBTZXJ2aWNlLT4-REI6IENoYW5nZSByb3dzCiAgICBTZXJ2aWNlLT4-REI6IENvbW1pdAogICAgREItLT4-U2VydmljZTogQ29tbWl0IGFja25vd2xlZGdlZAogICAgU2VydmljZS0-PlNlcnZpY2U6IENsb3NlIHNlc3Npb24gYW5kIHJlbGVhc2UgY29ubmVjdGlvbgogICAgU2VydmljZS0-Pkhvb2s6IGFmdGVyQ29tbWl0CiAgICBTZXJ2aWNlLT4-SG9vazogYWZ0ZXJDb21wbGV0aW9u%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="714" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The connection has already returned to the pool when &lt;code&gt;afterCommit&lt;/code&gt; runs. More database work needs a new transaction. If a callback fails, the original database change remains committed and the caller receives an exception saying so. Retrying the entire operation blindly can duplicate the work.&lt;/p&gt;

&lt;p&gt;These callbacks are synchronous and in memory. They don't make event delivery durable across a process crash. For a message that must eventually arrive, write an outbox record in the original transaction and deliver it separately.&lt;/p&gt;

&lt;p&gt;Wayline currently expresses a similar ordering with a transactional private method followed by its notification code. The compiler writes transaction handling into the method, so a call through &lt;code&gt;this&lt;/code&gt; doesn't bypass it. Completion hooks give other services an explicit place for that post-commit work.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/developer-guide/backend-data/" rel="noopener noreferrer"&gt;transaction guide&lt;/a&gt; covers nested scopes, failure outcomes and connection ownership. &lt;a href="https://github.com/codenameone/CodenameOne/pull/6007" rel="noopener noreferrer"&gt;Repositories and CASE expressions&lt;/a&gt; and &lt;a href="https://github.com/codenameone/CodenameOne/pull/6012" rel="noopener noreferrer"&gt;isolation and callbacks&lt;/a&gt; are separate additions that become more useful together: less query plumbing, with the important ordering still visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which Spring Data repository pattern would you want to bring to a native Java app first?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Create an Uber Clone in 1 Minute</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 23 Oct 2026 17:52:24 +0000</pubDate>
      <link>https://dev.to/codenameone/create-an-uber-clone-in-1-minute-421</link>
      <guid>https://dev.to/codenameone/create-an-uber-clone-in-1-minute-421</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcw8p0ztjoydecgfy9hd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdcw8p0ztjoydecgfy9hd.jpg" alt="Create your own ride-hailing app from the Wayline starting project" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A lot has changed since 2018, both in programming and in Codename One. Back then I wrote &lt;a href="https://www.amazon.com/Create-Uber-Clone-Days-mobile/dp/1983258784" rel="noopener noreferrer"&gt;Create an Uber Clone in 7 Days: Build a real world full stack mobile app in Java&lt;/a&gt;. Today you can start with the application those seven days were meant to teach you to build, then spend your time making it yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Wayline&lt;/strong&gt;, a full-stack, white-label ride-hailing application written in Java. Rider and driver apps, an administration console, payments, live tracking and push notifications are already connected to a Java backend. You get the application and its source, ready to adapt to your business. Open the &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;, choose Wayline, give it your own name and package, and generate the project. That's the one-minute task. Downloads and a first build depend on your machine; launching a real transport service still takes real work.&lt;/p&gt;

&lt;p&gt;The source is &lt;strong&gt;100% free&lt;/strong&gt;, under &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/wayline/LICENSE" rel="noopener noreferrer"&gt;MIT No Attribution, also called MIT-0&lt;/a&gt;. No royalties, no revenue share, no required subscription to use Wayline. You can modify it, sell your application and keep your changes private. Hosting and external services carry their own costs and terms. Your application belongs to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming This Week
&lt;/h2&gt;

&lt;p&gt;Today is the full application tour, including the code and the parts you would adapt for your own business. The next six posts look at the tools that help you build and ship that work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saturday, October 17:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/repositories-and-transactions-without-surprises/" rel="noopener noreferrer"&gt;Spring Data-Style Repositories Compiled to Native Code&lt;/a&gt;. Generated ORM repositories, isolation and completion callbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunday, October 18:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/compile-html-before-deployment/" rel="noopener noreferrer"&gt;Thymeleaf, Spring MVC and htmx for Native Java Backends&lt;/a&gt;. Thymeleaf-style templates, Spring MVC-style controllers and htmx.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday, October 19:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/headless-css-builds/" rel="noopener noreferrer"&gt;Build Your App Theme Without a Display Server&lt;/a&gt;. A fully headless compiler and the visual details worth checking after the change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday, October 20:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/desktop-tools-title-bars-processes-zip/" rel="noopener noreferrer"&gt;Give Your Desktop App a Useful Title Bar and Real Tools&lt;/a&gt;. Integrated title content on all three desktop OSs, the Process API and streaming ZIP support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wednesday, October 21:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/install-your-java-web-app-again/" rel="noopener noreferrer"&gt;Let Users Install Your Java Web App Again&lt;/a&gt;. PWA support returns to the ParparVM JavaScript port.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thursday, October 22:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/not-a-cloud-ide/" rel="noopener noreferrer"&gt;A Java IDE in Your Browser, With Your Files in Your Browser&lt;/a&gt;. An early preview of what I'm calling Not a Cloud IDE.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-rider-home.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-rider-home.png" alt="Choose a destination."&gt;&lt;/a&gt;Choose a destination.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-driver-offer.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-driver-offer.png" alt="Accept a ride as a driver."&gt;&lt;/a&gt;Accept a ride as a driver.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-dashboard.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-dashboard.png" alt="Manage the service."&gt;&lt;/a&gt;Manage the service.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A full-stack Java application you can make your own
&lt;/h2&gt;

&lt;p&gt;Wayline includes both sides of the service. The frontend implements the rider, driver and administrator workflows across mobile, desktop and browser builds. The backend owns authentication, ride matching, pricing, payments and the database. This is a white-label application you can brand and extend, with the operational details already expressed in code.&lt;/p&gt;

&lt;p&gt;The project has three modules: &lt;code&gt;common&lt;/code&gt; for the frontend, &lt;code&gt;backend&lt;/code&gt; for the server and &lt;code&gt;shared&lt;/code&gt; for the typed contract between them. Both sides use Java. You can follow a button press through an API call, a service and a repository without switching languages or maintaining a second set of request models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBVSVtKYXZhIGZyb250ZW5kOiByaWRlciwgZHJpdmVyIGFuZCBhZG1pbl0gLS0-IENvbnRyYWN0W1NoYXJlZCBBUEkgaW50ZXJmYWNlcyBhbmQgcmVjb3Jkc10KICAgIENvbnRyYWN0IC0tPiBFbmRwb2ludFtHZW5lcmF0ZWQgUkVTVCBjbGllbnQgYW5kIHNlcnZlciBjb250cmFjdF0KICAgIEVuZHBvaW50IC0tPiBTZXJ2aWNlc1tKYXZhIHNlcnZpY2VzIGFuZCBhdXRob3JpemF0aW9uXQogICAgU2VydmljZXMgLS0-IERCW0dlbmVyYXRlZCByZXBvc2l0b3JpZXMgYW5kIGRhdGFiYXNlXQogICAgU2VydmljZXMgLS0-IFByb3ZpZGVyc1tQYXltZW50cywgbWFwcyBhbmQgUHVzaCB2M10KICAgIFNlcnZpY2VzIC0tPiBMaXZlW0F1dGhlbnRpY2F0ZWQgV2ViU29ja2V0IHVwZGF0ZXNdCiAgICBMaXZlIC0tPiBVSQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBVSVtKYXZhIGZyb250ZW5kOiByaWRlciwgZHJpdmVyIGFuZCBhZG1pbl0gLS0-IENvbnRyYWN0W1NoYXJlZCBBUEkgaW50ZXJmYWNlcyBhbmQgcmVjb3Jkc10KICAgIENvbnRyYWN0IC0tPiBFbmRwb2ludFtHZW5lcmF0ZWQgUkVTVCBjbGllbnQgYW5kIHNlcnZlciBjb250cmFjdF0KICAgIEVuZHBvaW50IC0tPiBTZXJ2aWNlc1tKYXZhIHNlcnZpY2VzIGFuZCBhdXRob3JpemF0aW9uXQogICAgU2VydmljZXMgLS0-IERCW0dlbmVyYXRlZCByZXBvc2l0b3JpZXMgYW5kIGRhdGFiYXNlXQogICAgU2VydmljZXMgLS0-IFByb3ZpZGVyc1tQYXltZW50cywgbWFwcyBhbmQgUHVzaCB2M10KICAgIFNlcnZpY2VzIC0tPiBMaXZlW0F1dGhlbnRpY2F0ZWQgV2ViU29ja2V0IHVwZGF0ZXNdCiAgICBMaXZlIC0tPiBVSQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="896" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the booking method in the shared &lt;code&gt;RiderApi&lt;/code&gt; interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@POST&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/rides"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Body&lt;/span&gt; &lt;span class="nc"&gt;RideRequestDto&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;OnComplete&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RideDto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interface carries &lt;code&gt;@RestClient&lt;/code&gt;. The build generates its client implementation and the &lt;code&gt;RiderApiServer&lt;/code&gt; contract. Wayline's &lt;code&gt;RiderEndpoint&lt;/code&gt; implements that contract, injects the &lt;code&gt;Rides&lt;/code&gt; service and delegates the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RideDto&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RideRequestDto&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rides&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Caller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rider identity comes from the authenticated caller. The client supplies the trip it wants, and the server decides whether the signed-in user can request it. Spring-style dependency injection and authorization keep that decision in the backend; generated DTO serialization and API bindings keep the frontend and backend in agreement.&lt;/p&gt;

&lt;p&gt;You can run the server on the JVM while developing, then package it as a native executable with ParparVM. The frontend shares its screens and behavior across targets. The &lt;a href="https://www.codenameone.com/developer-guide/wayline-users-guide/" rel="noopener noreferrer"&gt;user's guide&lt;/a&gt; tours the application, while the &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/" rel="noopener noreferrer"&gt;implementor's guide&lt;/a&gt; follows each feature into its classes. All the Wayline screenshots below come from those guides and open at full size when clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the whole trip
&lt;/h2&gt;

&lt;p&gt;The application opens in the mode that fits the signed-in user. Riders get the booking screen, drivers get their shift, and administrators get the console. An account can have several roles and switch between them. These are distinct workflows within one shared application, so you don't maintain three unrelated clients.&lt;/p&gt;

&lt;p&gt;A new user registers, verifies a phone number and chooses whether to share usage statistics. The development profile displays verification codes instead of sending an SMS, so you can try the flow before connecting a provider.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-welcome.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-welcome.png" alt="Sign in, create an account or choose the server."&gt;&lt;/a&gt;Sign in, create an account or choose the server.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-register.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-register.png" alt="Register to ride or apply to drive."&gt;&lt;/a&gt;Register to ride or apply to drive.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-verify-phone.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-verify-phone.png" alt="Verify a phone number; development shows the code."&gt;&lt;/a&gt;Verify a phone number; development shows the code.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-analytics-consent.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-analytics-consent.png" alt="Choose whether this device shares usage statistics."&gt;&lt;/a&gt;Choose whether this device shares usage statistics.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The rider searches for a destination, sees the route and compares three vehicle categories with a fare for each. Home and Work shortcuts save repeated typing. Ride preferences cover quiet trips, accessibility and pets, and can restrict matching to women drivers based on the driver's profile.&lt;/p&gt;

&lt;p&gt;After requesting, the rider follows the approaching driver, sees the car and plate, and can call or message them. At the end, the flow moves to a rating, optional tip and itemized receipt.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-ride-quote.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-ride-quote.png" alt="Compare vehicle categories and quoted fares."&gt;&lt;/a&gt;Compare vehicle categories and quoted fares.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-ride-driver-on-the-way.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-ride-driver-on-the-way.png" alt="Follow the driver to pickup."&gt;&lt;/a&gt;Follow the driver to pickup.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-ride-receipt.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-ride-receipt.png" alt="Rate the completed ride and choose a tip."&gt;&lt;/a&gt;Rate the completed ride and choose a tip.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The screen follows the server's state. A button asks for a change; the server decides whether that change is valid. The repositories use conditional updates so competing actions can be settled by the database. That matters when a cancellation and an acceptance arrive together.&lt;/p&gt;

&lt;p&gt;The request flow is visible in &lt;code&gt;Rides&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RideDto&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideRequestDto&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rideChanged&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUESTED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;open&lt;/code&gt; is transactional. It serializes competing requests from the same rider before creating the ride; &lt;code&gt;attempt&lt;/code&gt; handles the retry path. Once that work returns, the service publishes the change and starts matching. The details that prevent two active rides belong here, where every client gets the same rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBSZXF1ZXN0W1JpZGVyIHJlcXVlc3RzIGEgcmlkZV0gLS0-IE1hdGNoW1NlcnZlciBvZmZlcnMgaXQgdG8gYSBkcml2ZXJdCiAgICBNYXRjaCAtLT4gQWNjZXB0W0RyaXZlciBhY2NlcHRzXQogICAgQWNjZXB0IC0tPiBQaWNrdXBbRHJpdmVyIGFycml2ZXNdCiAgICBQaWNrdXAgLS0-IFRyaXBbVHJpcCBpbiBwcm9ncmVzc10KICAgIFRyaXAgLS0-IENvbXBsZXRlW0NvbW1pdCBjb21wbGV0ZWQgcmlkZV0KICAgIENvbXBsZXRlIC0tPiBQYXltZW50W1NldHRsZSBwYXltZW50XQogICAgUGF5bWVudCAtLT4gUmVjZWlwdFtSZWNlaXB0IGFuZCByYXRpbmdd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBSZXF1ZXN0W1JpZGVyIHJlcXVlc3RzIGEgcmlkZV0gLS0-IE1hdGNoW1NlcnZlciBvZmZlcnMgaXQgdG8gYSBkcml2ZXJdCiAgICBNYXRjaCAtLT4gQWNjZXB0W0RyaXZlciBhY2NlcHRzXQogICAgQWNjZXB0IC0tPiBQaWNrdXBbRHJpdmVyIGFycml2ZXNdCiAgICBQaWNrdXAgLS0-IFRyaXBbVHJpcCBpbiBwcm9ncmVzc10KICAgIFRyaXAgLS0-IENvbXBsZXRlW0NvbW1pdCBjb21wbGV0ZWQgcmlkZV0KICAgIENvbXBsZXRlIC0tPiBQYXltZW50W1NldHRsZSBwYXltZW50XQogICAgUGF5bWVudCAtLT4gUmVjZWlwdFtSZWNlaXB0IGFuZCByYXRpbmdd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1883" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The normal journey is shown here. The application also handles declined offers, expiry, cancellation and payment failures.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the driver an application too
&lt;/h2&gt;

&lt;p&gt;A ride-hailing demo that stops at the passenger screen leaves most of the service for you to build. Wayline includes driver signup, document submission and review, then the actual working day.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-onboarding-documents.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-onboarding-documents.png" alt="Submit the documents needed to apply."&gt;&lt;/a&gt;Submit the documents needed to apply.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-onboarding-under-review.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-onboarding-under-review.png" alt="See where an application stands."&gt;&lt;/a&gt;See where an application stands.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-driver-offline.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-driver-offline.png" alt="Go online when ready to take rides."&gt;&lt;/a&gt;Go online when ready to take rides.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;An offer shows the pickup, destination, fare and a countdown. During a trip the driver sees the route and the next action, including the amount to collect for a cash ride. The earnings view shows daily totals and the balance recorded by the service.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-driver-on-trip.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-driver-on-trip.png" alt="Follow the trip and complete it."&gt;&lt;/a&gt;Follow the trip and complete it.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-driver-earnings.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-driver-earnings.png" alt="Inspect earnings and the recorded payout balance."&gt;&lt;/a&gt;Inspect earnings and the recorded payout balance.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;When the driver switches to a navigation app or locks the phone, the mobile background-location service keeps reporting during the shift. Reports are spaced by time and distance instead of forwarding every location change. iOS uses background location; Android uses a location foreground service. The browser and desktop preview don't reproduce that mobile background behavior.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Shift.begin()&lt;/code&gt; starts tracking and, on Android, creates the foreground service with a method reference for its background task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ForegroundService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CHANNEL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"You're online"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Wayline shares your location with riders while you drive."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;Shift:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;keepAlive&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Shift.end()&lt;/code&gt; removes the location watcher and stops that service when the driver goes offline. The same class throttles reports by distance and elapsed time, including a heartbeat for a parked car.&lt;/p&gt;

&lt;p&gt;There are ride widgets outside the main screen too. The rider's active trip appears as an iPhone Live Activity, an ongoing Android notification, or a small desktop/simulator window. A browser has no equivalent surface in Wayline. The &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/#wayline-live-activity" rel="noopener noreferrer"&gt;lock-screen section&lt;/a&gt; explains the distinction: the iPhone implementation updates when the app receives messages, and doesn't use ActivityKit's direct server push token. A suspended app can therefore leave the activity showing its last received state.&lt;/p&gt;

&lt;p&gt;The widget's layout is Java too. This excerpt from &lt;code&gt;RideCard.descriptor()&lt;/code&gt; supplies the compact iPhone presentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LiveActivityDescriptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RideActivityKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;KIND&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCompactLeading&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SurfaceText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${driver}"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;setFontSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMaxLines&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCompactTrailing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SurfaceText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${eta}"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;setFontSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setColor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SurfaceColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ACCENT&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete descriptor also supplies expanded and minimal layouts. &lt;code&gt;LiveActivity.start(descriptor(), now)&lt;/code&gt; starts it, and &lt;code&gt;current.update(now)&lt;/code&gt; sends new state. Wayline registers the resulting activity ID with the backend so later push messages can address it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live updates over WebSockets
&lt;/h2&gt;

&lt;p&gt;An open app needs to hear when the ride changes or the driver moves. &lt;code&gt;LiveChannel&lt;/code&gt; opens a WebSocket and hands its callbacks back to the UI event thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onConnect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opened&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onTextMessage&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onClose&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lost&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;callSerially&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lost&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mine&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the connection portion of &lt;code&gt;LiveChannel.open()&lt;/code&gt;. Its helpers handle reconnects, heartbeats and stale callbacks from previous connections. The app obtains a short-lived, single-use ticket through its authenticated API before connecting to &lt;code&gt;/ws/live&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The backend endpoint implements &lt;code&gt;WebSocket&lt;/code&gt; and is registered with &lt;code&gt;@WebSocketMapping("/ws/live")&lt;/code&gt;. It redeems the ticket, checks the account and adds the connection to &lt;code&gt;LiveHub&lt;/code&gt;. When a ride changes, the hub constructs the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ride"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"state"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sends that frame through &lt;code&gt;WebSocketSession.sendText&lt;/code&gt; to the rider, the driver and connected administrators. Driver positions have their own message type. Removing an admin role also closes that account's old connections, so a previously authorized socket cannot keep receiving fleet locations. The &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/" rel="noopener noreferrer"&gt;live channel guide&lt;/a&gt; traces both sides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push v3 callbacks and backend delivery
&lt;/h2&gt;

&lt;p&gt;The live WebSocket channel serves an app that is open. Push covers the rider with a phone in a pocket and the driver who has just been offered a job.&lt;/p&gt;

&lt;p&gt;The frontend uses the Push v3 &lt;code&gt;PushClient&lt;/code&gt; API. This is the registration code inside &lt;code&gt;Notifications.start()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PushClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PUSH_APP_KEY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Listener&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its &lt;code&gt;PushListener&lt;/code&gt; receives the token through &lt;code&gt;onRegistration&lt;/code&gt; and ride notifications through &lt;code&gt;onMessage&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onRegistration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PushSubscription&lt;/span&gt; &lt;span class="n"&gt;registered&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registered&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;signedIn&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message callback reads the payload and opens the relevant ride screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PushMessage&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rideId"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTitle&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ride&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Nav&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showRide&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Nav&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RIDER&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The screen fetches the current ride state rather than trusting an old notification. Silent messages can update the Live Activity without opening a screen. These callbacks run on the UI event thread.&lt;/p&gt;

&lt;p&gt;The app registers a device token for the signed-in account and removes that association on sign-out. A notification carries the ride ID and role so tapping it opens the right screen, which then reads the current ride from the server. Expiring driver offers use a short lifetime so a delayed notification doesn't look like a fresh job.&lt;/p&gt;

&lt;p&gt;On the backend, &lt;code&gt;RidePushes&lt;/code&gt; builds the message with ordinary Java calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;PushNotification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collapseKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ride-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rideId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"state"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CloudPushSender&lt;/code&gt; sends the built notification and device targets through &lt;code&gt;CloudPush&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;PushResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;accepted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAccepted&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The call runs on a background task so the ride request doesn't wait for the push service. Acceptance here means the service accepted the notification; it isn't proof that someone read it. Wayline also checks the latest ride state before composing a notification, preventing a delayed acceptance event from announcing a ride that has since been canceled.&lt;/p&gt;

&lt;p&gt;The server publishes ride changes after they commit. A push delivery failure doesn't undo an accepted ride. Those are small decisions that make a much bigger difference than adding another animation to the booking screen.&lt;/p&gt;

&lt;p&gt;Wayline includes the integration, while development logs notifications to the console. Real mobile push requires the app and server keys and platform credentials described in the &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/#wayline-push" rel="noopener noreferrer"&gt;push setup&lt;/a&gt;. Android push starts disabled so a generated project can build before you have a Firebase or Huawei configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The admin console is part of the product
&lt;/h2&gt;

&lt;p&gt;Someone has to approve drivers, answer a payment question and find out why a rider was blocked. Those jobs shouldn't require opening a database console.&lt;/p&gt;

&lt;p&gt;On a phone, administration is one screen at a time. The dashboard starts with work needing attention, then shows current activity and trends. An application review includes documents, and moderation records both the reason and the administrator responsible.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-trends.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-trends.png" alt="Inspect daily rides and fares."&gt;&lt;/a&gt;Inspect daily rides and fares.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-application.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-application.png" alt="Review a driver application."&gt;&lt;/a&gt;Review a driver application.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-user.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-user.png" alt="See an account and its moderation history."&gt;&lt;/a&gt;See an account and its moderation history.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;On a wide screen, the same app becomes a console with navigation and room for a list beside the selected item. This is a layout you can work in all day:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-dashboard.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-dashboard.png" alt="The wide dashboard puts activity and trends together."&gt;&lt;/a&gt;The wide dashboard puts activity and trends together.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-applications.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-applications.png" alt="Review applications without losing the list."&gt;&lt;/a&gt;Review applications without losing the list.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Flagging and blocking aren't the same operation. A flag calls attention to an account; a block signs it out and prevents access until it is lifted. The people view includes role changes, ride history and financial totals. The rides view lets an administrator inspect or cancel work and request a card refund where applicable.&lt;/p&gt;

&lt;p&gt;The backend guards the awkward cases too. Inside &lt;code&gt;Accounts.suspend()&lt;/code&gt;, an administrator cannot strand someone halfway through a ride by disabling the account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suspended&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;signIn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEnabled&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inRideUnderWay&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"That account is on a ride. Block it when the ride has ended"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operation runs in a transaction and records who changed the account and why. The administrator must end the ride first. That rule protects both participants even if moderation is invoked through a different client.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-user.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-user.png" alt="Search accounts, change roles and inspect history."&gt;&lt;/a&gt;Search accounts, change roles and inspect history.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-rides.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-rides.png" alt="Filter and inspect rides across the service."&gt;&lt;/a&gt;Filter and inspect rides across the service.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Pricing is editable: base fare, distance, time, minimum fare, fees, commission, and multipliers. The payments list distinguishes a completed refund from one still pending. The live map shows online cars, and the guide screenshot below captures its empty state rather than inventing a busy fleet.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-payments.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-payments.png" alt="Inspect charges and their payment state."&gt;&lt;/a&gt;Inspect charges and their payment state.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-pricing.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-pricing.png" alt="Set the service prices in the console."&gt;&lt;/a&gt;Set the service prices in the console.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-admin-desktop-live-map.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-admin-desktop-live-map.png" alt="The live map, here with no drivers online."&gt;&lt;/a&gt;The live map, here with no drivers online.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Billing includes the awkward cases
&lt;/h2&gt;

&lt;p&gt;The wallet stores payment methods, and receipts break down the fare. Development uses simulated cards with test behavior for success, decline and uncertain results. Real payment providers sit behind a &lt;code&gt;PaymentProvider&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-wallet.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-wallet.png" alt="Keep a default card or choose cash."&gt;&lt;/a&gt;Keep a default card or choose cash.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-receipt.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-receipt.png" alt="Read the fare breakdown and payment result."&gt;&lt;/a&gt;Read the fare breakdown and payment result.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;A completed ride and a settled payment are separate facts. Wayline commits the trip, calls the payment provider without holding that database transaction open, then records the result. Charges carry a key derived from the ride so retrying an uncertain request can reuse the same payment identity. This call from &lt;code&gt;Payments.settle()&lt;/code&gt; makes that key explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ride-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRider&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethodId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrency&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"Wayline ride"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The helper resolves the stored provider references and delegates to &lt;code&gt;PaymentProvider.charge&lt;/code&gt;. A definite decline becomes &lt;code&gt;FAILED&lt;/code&gt;; an I/O failure leaves the result &lt;code&gt;PENDING&lt;/code&gt; so recovery can ask again using the same key.&lt;/p&gt;

&lt;p&gt;If the server stops between those steps, a recovery pass revisits pending payments. A definite decline is treated differently from a lost response. Refunds can be pending too; the admin screen doesn't call the money returned just because a provider accepted the request.&lt;/p&gt;

&lt;p&gt;The included Stripe adapter uses hosted card setup so card details don't pass through the app's server. It has tests against stand-in provider responses, but hasn't been validated against a Stripe account. Before taking real payments, exercise its full flow with your provider and complete the missing production work described in the &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/#wayline-billing" rel="noopener noreferrer"&gt;billing section&lt;/a&gt;: webhooks, required cardholder authentication, driver transfers and your tax/invoice rules.&lt;/p&gt;

&lt;p&gt;In particular, &lt;strong&gt;Cash out currently records a payout in the ledger; it doesn't transfer money to a driver's bank&lt;/strong&gt;. That is a concrete extension point, not a hidden promise behind a finished-looking button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maps change the starting economics
&lt;/h2&gt;

&lt;p&gt;In 2018, native maps were central to this kind of app. We still support native maps, including the platform integration described in the guide. Wayline focuses on hosted map data rendered by Codename One's &lt;code&gt;MapView&lt;/code&gt;, which gives a small operator more control over the provider and the cost model.&lt;/p&gt;

&lt;p&gt;The default setup draws OpenStreetMap vector tiles from OpenFreeMap without an API key. Routes use the OSRM demo service, and place search goes through your backend to Photon. These are separate choices, not one inseparable maps account. The map factory selects the tile source and style directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;MapView&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MapView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;MvtTileSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openFreeMap&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tiles&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Look&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;MapStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MapStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setWheelZoom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;moveCamera&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zoom&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This excerpt comes from &lt;code&gt;Maps.create()&lt;/code&gt;. &lt;code&gt;Maps.useTiles(TileSource source)&lt;/code&gt; supplies a replacement source, which also lets the screenshot tests use bundled tiles instead of depending on a network service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBcHBbV2F5bGluZSBhcHBdIC0tPiBUaWxlc1tIb3N0ZWQgdmVjdG9yIHRpbGVzXQogICAgQXBwIC0tPiBSb3V0ZXNbUm91dGluZyBzZXJ2aWNlXQogICAgQXBwIC0tPiBTZXJ2ZXJbWW91ciBiYWNrZW5kXQogICAgU2VydmVyIC0tPiBTZWFyY2hbUGxhY2Ugc2VhcmNoIHNlcnZpY2Vd%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBBcHBbV2F5bGluZSBhcHBdIC0tPiBUaWxlc1tIb3N0ZWQgdmVjdG9yIHRpbGVzXQogICAgQXBwIC0tPiBSb3V0ZXNbUm91dGluZyBzZXJ2aWNlXQogICAgQXBwIC0tPiBTZXJ2ZXJbWW91ciBiYWNrZW5kXQogICAgU2VydmVyIC0tPiBTZWFyY2hbUGxhY2Ugc2VhcmNoIHNlcnZpY2Vd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="663" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can point the app at your own tile and routing services, or choose hosted providers. A small business can begin without a map SDK billing account and later choose whether it wants a provider bill or the work of hosting its own data services. Self-hosting still costs money and operational time; public routing and search servers aren't a production service guarantee. The default routes don't include live traffic.&lt;/p&gt;

&lt;p&gt;That changes the question from “which map vendor must this application use?” to “which service fits this region and volume?” The &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/#wayline-maps-data" rel="noopener noreferrer"&gt;maps chapter of the implementor's guide&lt;/a&gt; shows the replacement points and the decisions to make before launch.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-rider-home-dark.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-rider-home-dark.png" alt="The vector map follows the dark theme."&gt;&lt;/a&gt;The vector map follows the dark theme.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-rider-desktop.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-rider-desktop.png" alt="The rider experience in a wide desktop window."&gt;&lt;/a&gt;The rider experience in a wide desktop window.&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Readable Java, even when an agent makes the change
&lt;/h2&gt;

&lt;p&gt;You can hand an agent the job of changing a fare rule, adding a service area or branding the screens. Java's strict types and straightforward structure help keep that work readable. A request has a declared type, a service has explicit dependencies, and a repository states what it reads. A changed method signature must agree with its callers.&lt;/p&gt;

&lt;p&gt;Wayline gives the agent the same things I want as a developer: named responsibilities, conventional code, tests and a development MCP endpoint. You can review the resulting diff and follow the behavior through the application. The code remains yours to understand and maintain.&lt;/p&gt;

&lt;p&gt;Spring Security users should recognize the authorization rules. This excerpt is part of &lt;code&gt;SecurityConfig&lt;/code&gt;'s API chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AntPathRequestMatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;antMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"/api/account/register"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;permitAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/driver/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;hasRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DRIVER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/admin/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;hasRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ADMIN&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete chain configures JWT authentication and other protections. The server reads the caller from its security context, rather than accepting a rider identity supplied by the client. The APIs use Codename One packages and supported Spring-style conventions; this isn't a Spring runtime hidden in the project.&lt;/p&gt;

&lt;p&gt;The data layer is similarly recognizable. Wayline's &lt;code&gt;RideRepository&lt;/code&gt; begins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Repository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;RideRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CrudRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="nf"&gt;findFirstByRiderAndStateIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rider&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideState&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="nf"&gt;findFirstByDriverAndStateIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideState&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build writes the query implementation. Services decide what changes belong in one transaction. If you prefer a direct data access object, the backend also generates DAOs. This separate example from the &lt;a href="https://www.codenameone.com/developer-guide/backend-data/" rel="noopener noreferrer"&gt;database guide&lt;/a&gt; shows that lower-level style using its &lt;code&gt;Reminder&lt;/code&gt; entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Dao&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Reminder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;reminders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dao&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Reminder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Reminder&lt;/span&gt; &lt;span class="n"&gt;reminder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Reminder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;reminder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"renew the certificate"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;reminder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;due&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;reminders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reminder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Reminder&lt;/span&gt; &lt;span class="n"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reminders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reminder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;stored&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;reminders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stored&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the frontend, a helper creates an ordinary component, gives it a test name and attaches an action. This is Wayline's button helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;uiid&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;ActionListener&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uiid&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UIID connects it to CSS. The primary style uses shared variables for branding and shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;WlPrimary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--brand-contrast&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--brand&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--radius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"native:MainBold"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.9mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.75mm&lt;/span&gt; &lt;span class="m"&gt;4mm&lt;/span&gt; &lt;span class="m"&gt;2.75mm&lt;/span&gt; &lt;span class="m"&gt;4mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.6mm&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0.8mm&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variables and their dark variants live near the top of &lt;code&gt;theme.css&lt;/code&gt;. An agent changing your brand color has a clear place to edit, and a human reviewing the change can see whether it touched anything else.&lt;/p&gt;

&lt;p&gt;You can inspect these excerpts in the &lt;a href="https://github.com/codenameone/CodenameOne/tree/master/scripts/wayline" rel="noopener noreferrer"&gt;Wayline source&lt;/a&gt;. The implementor's guide links each feature to its implementation and explains how to adapt it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Settings are part of making the app yours
&lt;/h2&gt;

&lt;p&gt;Wayline includes light and dark themes, distance units and translations into English, Spanish, French, German, and Hebrew. The Hebrew layout runs right to left. Wide settings screens stay in a readable column instead of stretching every field across the monitor.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-settings.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-settings.png" alt="Account, ride and appearance preferences."&gt;&lt;/a&gt;Account, ride and appearance preferences.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-settings-dark.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-settings-dark.png" alt="The same settings in dark mode."&gt;&lt;/a&gt;The same settings in dark mode.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-settings-hebrew.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-settings-hebrew.png" alt="Hebrew text and right-to-left layout."&gt;&lt;/a&gt;Hebrew text and right-to-left layout.&lt;br&gt;
&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-settings-analytics.png" rel="noopener noreferrer"&gt;&lt;img width="420" height="909" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-settings-analytics.png" alt="Usage statistics remain a device-level choice."&gt;&lt;/a&gt;Usage statistics remain a device-level choice.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/wayline-settings-desktop.png" rel="noopener noreferrer"&gt;&lt;img width="800" height="471" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fdeveloper-guide%2Fimg%2Fwayline-settings-desktop.png" alt="Settings adapt to a desktop window."&gt;&lt;/a&gt;Settings adapt to a desktop window.&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Some settings are hooks for your product policy. The promotions switch is stored, but Wayline sends no promotions. The emergency contact and automatic trip-sharing preference are stored without sending messages; the ride's Share action is separate. The guide calls these out so you can decide what they should mean for your service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it, then change one thing
&lt;/h2&gt;

&lt;p&gt;Generate the Wayline template from the &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;. It renames the Java package, artifacts and application configuration for your project. With JDK 17 or newer and Maven installed, start the development backend from the generated project's root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CN1_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev backend/server.sh run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal, start the simulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn verify &lt;span class="nt"&gt;-Psimulator&lt;/span&gt; &lt;span class="nt"&gt;-DskipTests&lt;/span&gt; &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;javase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The development profile uses an in-memory database, demo accounts and simulated external services. The &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/#wayline-launch" rel="noopener noreferrer"&gt;launch instructions&lt;/a&gt; list the accounts and show how to run the rider and driver together. Keep that profile for development.&lt;/p&gt;

&lt;p&gt;For a browser version hosted by the same backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;backend/server.sh web
&lt;span class="nv"&gt;CN1_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev backend/server.sh run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://localhost:8080/&lt;/code&gt;. The first command builds the browser application; rerun it after changing the client. Server packaging can also produce a native binary.&lt;/p&gt;

&lt;p&gt;Before handing a change to an agent, give it a useful check. The backend test command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nt"&gt;-pl&lt;/span&gt; shared,backend &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;backend &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suite covers roles, the ride lifecycle and payment outcomes, among other service behavior. Native compiled test execution is available as a &lt;strong&gt;local-only&lt;/strong&gt; option in the testing guide. The development profile's &lt;code&gt;/mcp&lt;/code&gt; endpoint gives an agent another way to inspect the backend; it isn't an endpoint to expose publicly without the production authentication configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a service your market needs
&lt;/h2&gt;

&lt;p&gt;I see this as an opening for startups whose idea begins with ride hailing. Airport transfers, a specialist fleet or transport for a particular community can start here, then build the part that makes the service useful to those people. &lt;strong&gt;Make something better.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A taxi company can run its own branded, hosted app with central management. The &lt;a href="https://www.codenameone.com/developer-guide/wayline-implementors-guide/" rel="noopener noreferrer"&gt;taxi-station adaptation&lt;/a&gt; discusses a fixed fleet, manual dispatch and phone bookings as changes to make. Those additions aren't all finished screens in Wayline, but the guide shows where they belong.&lt;/p&gt;

&lt;p&gt;I can see a cottage industry around that work: developers who adapt and operate these applications for local businesses. A small operator gets more freedom over its brand and service rules, and the developer can spend time on the local requirements instead of recreating the wallet and sign-in flow. Whether that becomes a business depends on finding operators whose problems you can solve. The starting code is now available to try.&lt;/p&gt;




&lt;h2&gt;
  
  
  More This Week
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Spring Data-style repositories, statically compiled
&lt;/h3&gt;

&lt;p&gt;A finder such as &lt;code&gt;findFirstByDriverAndStateIn&lt;/code&gt; puts a query next to the rest of the entity's data access. The new repository generator supplies its implementation, including support for declared queries and &lt;code&gt;CASE&lt;/code&gt; expressions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Repository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;RideRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;CrudRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="nf"&gt;findFirstByDriverAndStateIn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RideState&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get Spring Data-style finder methods and familiar Hibernate persistence conventions with generated Java implementations. Native builds compile those implementations ahead of time. There is no repository classpath scan or runtime proxy generation, and the resulting calls are available to the compiler for optimization.&lt;/p&gt;

&lt;p&gt;Transaction isolation and callbacks deal with the next question: when can another part of the system act on that result? A completion callback runs after the database acknowledges the commit. If the callback fails, the database change is still committed, so retrying the entire operation is the wrong default.&lt;/p&gt;

&lt;p&gt;Saturday's &lt;a href="https://www.codenameone.com/blog/repositories-and-transactions-without-surprises/" rel="noopener noreferrer"&gt;repository and transaction article&lt;/a&gt; covers both the convenience and the boundaries, including bulk updates, supported isolation levels and durable event delivery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thymeleaf, Spring MVC and htmx in a native backend
&lt;/h3&gt;

&lt;p&gt;Thymeleaf-style templates, Spring MVC-style controllers and htmx make a useful combination for a fast server-rendered admin site. Our implementation compiles the supported template syntax into Java renderers, then into the native backend. The controller can return a page or a fragment for htmx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Htmx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"products :: catalog"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"products"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typed model access becomes direct Java access. Production requests execute compiled rendering code, without parsing templates or interpreting property expressions. That is the performance advantage of doing the template work during the build.&lt;/p&gt;

&lt;p&gt;Sunday's &lt;a href="https://www.codenameone.com/blog/compile-html-before-deployment/" rel="noopener noreferrer"&gt;Thymeleaf and Spring MVC article&lt;/a&gt; follows a working catalog through controllers, form errors and partial updates. It also explains what this subset leaves out.&lt;/p&gt;

&lt;h3&gt;
  
  
  A stylesheet shouldn't need a monitor
&lt;/h3&gt;

&lt;p&gt;The CSS compiler is fully headless. The same &lt;code&gt;mvn cn1:css&lt;/code&gt; goal can run on a worker without a display server or framebuffer setup. Removing that machinery removes startup work and display-dependent assumptions from theme compilation.&lt;/p&gt;

&lt;p&gt;This is also a substantial change to an old compiler with fragile corners. Fonts, image borders and physical-unit conversions deserve visual checks even when a build succeeds. Monday's &lt;a href="https://www.codenameone.com/blog/headless-css-builds/" rel="noopener noreferrer"&gt;headless CSS article&lt;/a&gt; shows the workflow and what to compare.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the desktop title area
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-16/native-editor-titlebar.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw9vytvg8omo9mlbav23e.jpg" alt="A native macOS editor puts Save, Undo, the document title and Search in the same row as its window controls" width="799" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The title area of our native macOS editor prototype. Document actions share the top row with the OS controls, leaving the ribbon and document below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Putting Save, Search or document controls in the window title area is a niche feature. If you're building an editor, it can save a whole row. Codename One supports it in the native macOS, Windows and Linux ports while keeping real OS window controls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToolbar&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setTitleComponent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With custom title mode selected, the framework reserves room for the native controls and separates interactive components from draggable background. Tuesday's &lt;a href="https://www.codenameone.com/blog/desktop-tools-title-bars-processes-zip/" rel="noopener noreferrer"&gt;desktop tools article&lt;/a&gt; explains the differences between the three operating systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run an external tool from Java
&lt;/h3&gt;

&lt;p&gt;The new shared &lt;code&gt;com.codename1.io.ProcessBuilder&lt;/code&gt; started with backend needs, then extended naturally to desktop apps. You can launch an installed command, stream its input and output, wait with a timeout and inspect its exit status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ProcessBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupported&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mobile and browser targets return no process-launch capability. Tuesday's article covers that boundary and the output-draining pattern that keeps a full pipe from hanging the worker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export a ZIP without inventing an archive format
&lt;/h3&gt;

&lt;p&gt;The ParparVM runtime now provides streaming &lt;code&gt;java.util.zip&lt;/code&gt; APIs, including ZIP, GZIP, and checksums. An app can export data through the familiar stream interfaces; a backend can write an archive straight to its response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putNextEntry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZipEntry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.txt"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Completed rides\n"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closeEntry&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This excerpt assumes an open &lt;code&gt;ZipOutputStream&lt;/code&gt;. The desktop tools article also shows the helper for preserving an executable launcher's Unix permissions, and the limits of the streaming subset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the web app again
&lt;/h3&gt;

&lt;p&gt;PWA support disappeared during the move from TeaVM to ParparVM. JavaScript builds now generate the manifest, icons and service worker again, giving supporting browsers an installable application and cached offline launch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBCdW5kbGVbR2VuZXJhdGVkIGFwcCBidW5kbGVdIC0tPiBDYWNoZVtTZXJ2aWNlIHdvcmtlciBjYWNoZV0KICAgIENhY2hlIC0tPiBMYXVuY2hbTGF1bmNoIHdpdGhvdXQgdGhlIG5ldHdvcmtdCiAgICBMYXVuY2ggLS0-IE9ubGluZVtTZXJ2ZXIgZmVhdHVyZXMgc3RpbGwgbmVlZCBhIGNvbm5lY3Rpb25d%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBCdW5kbGVbR2VuZXJhdGVkIGFwcCBidW5kbGVdIC0tPiBDYWNoZVtTZXJ2aWNlIHdvcmtlciBjYWNoZV0KICAgIENhY2hlIC0tPiBMYXVuY2hbTGF1bmNoIHdpdGhvdXQgdGhlIG5ldHdvcmtdCiAgICBMYXVuY2ggLS0-IE9ubGluZVtTZXJ2ZXIgZmVhdHVyZXMgc3RpbGwgbmVlZCBhIGNvbm5lY3Rpb25d%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1113" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wednesday's &lt;a href="https://www.codenameone.com/blog/install-your-java-web-app-again/" rel="noopener noreferrer"&gt;PWA article&lt;/a&gt; covers deployment, cache updates and the difference between an offline UI and a ride request that still needs a server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not a Cloud IDE
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://cloud.codenameone.com/ide/" rel="noopener noreferrer"&gt;experimental browser IDE&lt;/a&gt; opens a Java project with an editor and a running preview. Compilation and execution happen in the page, and files stay in this browser's storage. The cloud build server does its usual job when you explicitly request a platform build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-16/browser-ide-preview.jpg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foskwo717a9kuh50glxwy.jpg" alt="The experimental browser IDE with Java source and its running starter app" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a very early preview. It won't replace IntelliJ or reproduce all of Java inside JavaScript. It can let someone try an app before installing a development environment, then download the project when they're ready to continue locally. Thursday's &lt;a href="https://www.codenameone.com/blog/not-a-cloud-ide/" rel="noopener noreferrer"&gt;IDE tour&lt;/a&gt; shows that path and explains browser-local storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where is this going?
&lt;/h2&gt;

&lt;p&gt;Everywhere. Wayline is the first step in a wider collection of full-stack application templates in Initializr. We plan to do this for many more applications, including delivery: choose a template, generate your own project, then build the service your market needs. Raising the starting point is good for the whole market. Your moat can grow in the part competitors can't get from a template: the service you provide, the relationships you build and the problems you understand better. Starting with an application as substantial as Wayline gives you more time to build that advantage. I want to see what you make of it.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Smaller Simpler Maven Projects</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:57:54 +0000</pubDate>
      <link>https://dev.to/codenameone/smaller-simpler-maven-projects-4ca5</link>
      <guid>https://dev.to/codenameone/smaller-simpler-maven-projects-4ca5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3pv7v3woi0q42qnuclsi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3pv7v3woi0q42qnuclsi.jpg" alt="A smaller Maven project grows platform modules as needed" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening a new application should show you the application. A directory full of platform modules makes you learn the build's structure before you have written a screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/gradle-smaller-projects/" rel="noopener noreferrer"&gt;Last week we added smaller Gradle projects&lt;/a&gt;. Maven now starts small too. The application archetype defaults to the root POM and &lt;code&gt;common&lt;/code&gt;, with no platform modules. You can still run the simulator and build the target apps. This closes our &lt;a href="https://www.codenameone.com/blog/android-apps-beyond-android/" rel="noopener noreferrer"&gt;weekly release series&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The default tree matches the first task
&lt;/h2&gt;

&lt;p&gt;The relevant part of a new project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
    pom.xml
    common/
        pom.xml
        codenameone_settings.properties
        src/main/java/
        src/main/resources/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a shortened tree, not a count of every generated resource or launcher script. The structural count is &lt;strong&gt;two POMs&lt;/strong&gt;. Source, CSS and resources remain where an existing Maven user expects them.&lt;/p&gt;

&lt;p&gt;When a platform has no module, &lt;code&gt;common&lt;/code&gt; handles its build. These commands keep their meaning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn cn1:run
mvn package &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;android
mvn package &lt;span class="nt"&gt;-Pexecutable-jar&lt;/span&gt; &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;javase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The desktop executable JAR goes under &lt;code&gt;common/target&lt;/code&gt; in this layout. Platform modules become useful when a target needs independent dependencies or plugins, rather than because every possible target needs a directory on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  A native implementation does not need another POM
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mvn cn1:generate-native-interfaces&lt;/code&gt; still creates platform-specific source directories. An Android implementation can live under &lt;code&gt;android/src/main/java&lt;/code&gt;, and an iOS implementation under &lt;code&gt;ios/src/main/objectivec&lt;/code&gt;, without either directory becoming a Maven module.&lt;/p&gt;

&lt;p&gt;The build treats a platform as a separate module when &lt;code&gt;&amp;lt;platform&amp;gt;/pom.xml&lt;/code&gt; exists. Native source directories alone do not make that decision. JavaSE implementations are compiled into &lt;code&gt;common/target/cn1-javase/classes&lt;/code&gt;, separate from application classes, so they are not accidentally uploaded into a device build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBDb21tb25bQ29tbW9uIGFwcGxpY2F0aW9uIG1vZHVsZV0gLS0-IEFuZHJvaWRbQW5kcm9pZCBidWlsZF0KICAgIENvbW1vbiAtLT4gSU9TW2lPUyBidWlsZF0KICAgIENvbW1vbiAtLT4gRGVza3RvcFtEZXNrdG9wIGJ1aWxkXQogICAgTmF0aXZlW05hdGl2ZSBzb3VyY2UgZGlyZWN0b3JpZXNdIC0tPiBDb21tb24KICAgIFNlcGFyYXRlW09wdGlvbmFsIHBsYXRmb3JtIFBPTV0gLS0-IEN1c3RvbVtTZXBhcmF0ZSBwbGF0Zm9ybSBjb25maWd1cmF0aW9uXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBDb21tb25bQ29tbW9uIGFwcGxpY2F0aW9uIG1vZHVsZV0gLS0-IEFuZHJvaWRbQW5kcm9pZCBidWlsZF0KICAgIENvbW1vbiAtLT4gSU9TW2lPUyBidWlsZF0KICAgIENvbW1vbiAtLT4gRGVza3RvcFtEZXNrdG9wIGJ1aWxkXQogICAgTmF0aXZlW05hdGl2ZSBzb3VyY2UgZGlyZWN0b3JpZXNdIC0tPiBDb21tb24KICAgIFNlcGFyYXRlW09wdGlvbmFsIHBsYXRmb3JtIFBPTV0gLS0-IEN1c3RvbVtTZXBhcmF0ZSBwbGF0Zm9ybSBjb25maWd1cmF0aW9uXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="769" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This also keeps the Android migration introduced on Friday approachable. Imported Android sources live under &lt;code&gt;common/src/main/android&lt;/code&gt;; your initial port need not begin by learning every target module's POM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for the structure you need
&lt;/h2&gt;

&lt;p&gt;The archetype's &lt;code&gt;platformModules&lt;/code&gt; property defaults to &lt;code&gt;none&lt;/code&gt;. &lt;code&gt;all&lt;/code&gt; restores the full platform layout, and a comma-separated value such as &lt;code&gt;javase,android&lt;/code&gt; creates only those modules. Its &lt;code&gt;projectType&lt;/code&gt; defaults to &lt;code&gt;app&lt;/code&gt;; &lt;code&gt;app-with-backend&lt;/code&gt; adds the server, and &lt;code&gt;backend-only&lt;/code&gt; creates a standalone server project.&lt;/p&gt;

&lt;p&gt;For example, these are &lt;strong&gt;options to append to your normal archetype generation command&lt;/strong&gt;, not commands on their own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-DplatformModules=javase,android
-DprojectType=app-with-backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A backend-only project has its POM and &lt;code&gt;src/main/java&lt;/code&gt; at the root. It needs no empty client beside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw cn1:backend
./mvnw cn1:backend-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minimal default applies to newly generated projects using the updated archetype. It does not delete modules from an existing checkout. Before simplifying an older project, check for platform-specific build configuration rather than assuming its POMs are empty scaffolding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less structure to explain and inspect
&lt;/h2&gt;

&lt;p&gt;I still prefer Maven's explicit XML for build configuration. I also prefer having less of it to read. Those preferences can coexist.&lt;/p&gt;

&lt;p&gt;A smaller project tree helps a new contributor find the code that matters. It also leaves fewer generated configuration files for an LLM to misread when asked to change one screen. That is a reduction in surrounding material, not a guarantee that generated code is correct. Review the diff and run the relevant application behavior just as before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5937" rel="noopener noreferrer"&gt;PR #5937&lt;/a&gt; introduced these project shapes. The current &lt;a href="https://www.codenameone.com/developer-guide/#maven-minimal-layout" rel="noopener noreferrer"&gt;archetype reference&lt;/a&gt; defines how target selection and native directories work. Start with the default, then add a platform module when you have a concrete setting to put in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;How many of your platform POMs contain configuration you actually maintain?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>PGO for Faster Apps and Generational/Adaptive GC</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Wed, 21 Oct 2026 18:01:09 +0000</pubDate>
      <link>https://dev.to/codenameone/pgo-for-faster-apps-and-generationaladaptive-gc-122e</link>
      <guid>https://dev.to/codenameone/pgo-for-faster-apps-and-generationaladaptive-gc-122e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwgzcsgl3lu1w3embar88.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwgzcsgl3lu1w3embar88.jpg" alt="Profile-guided builds and adaptive collection reduce native application work" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your native compiler can optimize an app more effectively when it knows which code actually runs. Profile-guided optimization, or PGO, gives it that information. The build service runs an instrumented version of your application, records its execution and uses the profile to compile the binary your users receive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PGO is now a Pro and Enterprise cloud-build feature for iOS, native macOS and native Linux.&lt;/strong&gt; You enable it with a build hint; the service handles the extra compilation and training. It is a way to improve native performance without rewriting application logic or managing a profiling pipeline yourself.&lt;/p&gt;

&lt;p&gt;This release also adds adaptive generational garbage collection to ParparVM. We will start with PGO and how to use it, then look at the GC changes and the memory cost behind their CPU improvements. This continues our &lt;a href="https://www.codenameone.com/blog/android-apps-beyond-android/" rel="noopener noreferrer"&gt;weekly release series&lt;/a&gt; and &lt;a href="https://www.codenameone.com/blog/parparvm-four-byte-header/" rel="noopener noreferrer"&gt;last week's object-layout work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let your app guide the compiler
&lt;/h2&gt;

&lt;p&gt;An ordinary optimizing compiler has to estimate which branches are common and which functions are worth inlining. PGO replaces some of those estimates with counts from an actual run. Frequently executed code can be placed together and optimized for speed, while code outside those paths can stay compact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBTb3VyY2VbQXBwbGljYXRpb24gYW5kIHJ1bnRpbWUgY29kZV0gLS0-IEluc3RydW1lbnRbQ29tcGlsZSB3aXRoIGNvdW50ZXJzXQogICAgSW5zdHJ1bWVudCAtLT4gVHJhaW5bUnVuIHRoZSBhcHAgb24gdGhlIGJ1aWxkIHNlcnZlcl0KICAgIFRyYWluIC0tPiBQcm9maWxlW0Z1bmN0aW9uIGFuZCBicmFuY2ggY291bnRzXQogICAgUHJvZmlsZSAtLT4gQ29tcGlsZVtDb21waWxlIGFnYWluIHVzaW5nIHRoZSBwcm9maWxlXQogICAgQ29tcGlsZSAtLT4gRGVsaXZlcltEZWxpdmVyIG9wdGltaXplZCBuYXRpdmUgYXBwXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBTb3VyY2VbQXBwbGljYXRpb24gYW5kIHJ1bnRpbWUgY29kZV0gLS0-IEluc3RydW1lbnRbQ29tcGlsZSB3aXRoIGNvdW50ZXJzXQogICAgSW5zdHJ1bWVudCAtLT4gVHJhaW5bUnVuIHRoZSBhcHAgb24gdGhlIGJ1aWxkIHNlcnZlcl0KICAgIFRyYWluIC0tPiBQcm9maWxlW0Z1bmN0aW9uIGFuZCBicmFuY2ggY291bnRzXQogICAgUHJvZmlsZSAtLT4gQ29tcGlsZVtDb21waWxlIGFnYWluIHVzaW5nIHRoZSBwcm9maWxlXQogICAgQ29tcGlsZSAtLT4gRGVsaXZlcltEZWxpdmVyIG9wdGltaXplZCBuYXRpdmUgYXBwXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1781" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The profile applies to translated Java, Codename One's runtime and your native sources. The instrumented binary stays on the build server. Your users receive the second build, without the training counters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn on PGO for your target
&lt;/h2&gt;

&lt;p&gt;Add the relevant hint to &lt;code&gt;codenameone_settings.properties&lt;/code&gt;. This example enables it for iOS and sets a 90-second training period:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.ios.pgo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;codename1.arg.pgo.trainingSeconds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each target has its own switch:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build hint&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Training environment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.arg.ios.pgo=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;iOS device app&lt;/td&gt;
&lt;td&gt;Temporary iOS simulator on the build server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.arg.macos.pgo=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native macOS app&lt;/td&gt;
&lt;td&gt;Build server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.arg.linux.pgo=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native Linux app&lt;/td&gt;
&lt;td&gt;Build server with a virtual display&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The training period defaults to 60 seconds. Values below 10 or above 300 are brought into that range. Increasing it gives the application longer to exercise its startup work; it does not create user interactions.&lt;/p&gt;

&lt;p&gt;If you already subscribe to Pro or Enterprise, enable the hint and submit a supported cloud build. PGO is included in those plans. You can &lt;a href="https://www.codenameone.com/pricing/" rel="noopener noreferrer"&gt;compare plans here&lt;/a&gt;. These hints belong to the cloud build service; local builds do not consume them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the training run useful work
&lt;/h2&gt;

&lt;p&gt;The app starts from a clean state and runs unattended. Resource loading, initial layout and painting can contribute to the profile, as can animations, timers and network work that start automatically. Repeated work is particularly useful because the compiler can identify the paths that dominate execution.&lt;/p&gt;

&lt;p&gt;Nobody signs in or taps a button during training. If the app stops at a login screen, the profile describes that screen. Code behind it remains correct, but the compiler has no evidence that it is an important execution path. Before enabling PGO, run the app from a clean installation and check what it does without input.&lt;/p&gt;

&lt;p&gt;PGO adds build time because it requires two compilations and a training run. Start with a normal Release build and record the time for a representative task. Repeat that task with the profile-guided build on the same device. Check memory and the result of the task as well as elapsed time. The gain depends on how much of your workload the training run reached; we are not assigning a universal speedup percentage to it.&lt;/p&gt;

&lt;p&gt;A requested profile-guided build either produces that binary or fails with a reason. An empty profile, a crash during training or an incompatible build mode does not silently produce an ordinary build. The &lt;a href="https://www.codenameone.com/developer-guide/performance/" rel="noopener noreferrer"&gt;performance guide&lt;/a&gt; lists the settings, training rules and incompatible configurations, including debug and simulator-only modes. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5945" rel="noopener noreferrer"&gt;PR #5945&lt;/a&gt; introduced the build support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaptive GC reduces the work left to run
&lt;/h2&gt;

&lt;p&gt;PGO improves the generated native code. The collector changes reduce how much work that code needs to do. In our allocation workload, the concurrent collector spent time tracing fresh objects and sweeping their slots even when almost none survived.&lt;/p&gt;

&lt;p&gt;ParparVM now has an adaptive generational phase. Holding more memory can reduce collection frequency, but it can also make the next full trace or transition much worse. The standard Codename One benchmarks use &lt;strong&gt;no PGO&lt;/strong&gt;, so the following GC results measure the runtime changes separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collect survivors instead of revisiting every allocation
&lt;/h2&gt;

&lt;p&gt;A generational collector concentrates frequent work on young objects, based on the observation that many die quickly. References from older objects to young ones must still be tracked, and surviving objects cannot simply disappear when a young page is reset.&lt;/p&gt;

&lt;p&gt;Our hybrid chooses between the existing concurrent path and stop-the-world generational cycles. On eligible multicore processes, it enters after repeated low-survival cycles with allocation demand and a concurrent collector busy for at least half the interval between cycles. &lt;strong&gt;Low available memory is an exit condition&lt;/strong&gt;, not the trigger to enter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBDb25jdXJyZW50W0NvbmN1cnJlbnQgY29sbGVjdGlvbl0gLS0-IENoZWNre0xvdyBzdXJ2aXZhbCBhbmQgaGlnaCBjb2xsZWN0b3IgZHV0eT99CiAgICBDaGVjayAtLT58UmVwZWF0ZWQsIGVsaWdpYmxlIHRocmVhZHMsIG1lbW9yeSBhdmFpbGFibGV8IE1ham9yW1N0b3AtdGhlLXdvcmxkIG1ham9yXQogICAgQ2hlY2sgLS0-fE5vfCBDb25jdXJyZW50CiAgICBNYWpvciAtLT4gTWlub3JbWW91bmctZ2VuZXJhdGlvbiBtaW5vciBjeWNsZXNdCiAgICBNaW5vciAtLT4gRXhpdHtIaWdoIHN1cnZpdmFsLCBsb25nIG1ham9yIG1hcmssIHF1aWV0IG9yIGxvdyBtZW1vcnk_fQogICAgRXhpdCAtLT58WWVzfCBDb25jdXJyZW50CiAgICBFeGl0IC0tPnxOb3wgTWlub3I%3D%3Ftype%3Dpng%26bgColor%3Dffffff" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBDb25jdXJyZW50W0NvbmN1cnJlbnQgY29sbGVjdGlvbl0gLS0-IENoZWNre0xvdyBzdXJ2aXZhbCBhbmQgaGlnaCBjb2xsZWN0b3IgZHV0eT99CiAgICBDaGVjayAtLT58UmVwZWF0ZWQsIGVsaWdpYmxlIHRocmVhZHMsIG1lbW9yeSBhdmFpbGFibGV8IE1ham9yW1N0b3AtdGhlLXdvcmxkIG1ham9yXQogICAgQ2hlY2sgLS0-fE5vfCBDb25jdXJyZW50CiAgICBNYWpvciAtLT4gTWlub3JbWW91bmctZ2VuZXJhdGlvbiBtaW5vciBjeWNsZXNdCiAgICBNaW5vciAtLT4gRXhpdHtIaWdoIHN1cnZpdmFsLCBsb25nIG1ham9yIG1hcmssIHF1aWV0IG9yIGxvdyBtZW1vcnk_fQogICAgRXhpdCAtLT58WWVzfCBDb25jdXJyZW50CiAgICBFeGl0IC0tPnxOb3wgTWlub3I%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="468" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An all-dead young page can be reset in constant time. The allocator's bump path is inline again, and accounting moves from each object to each page where possible. Those changes remove work rather than merely asking another CPU to do it.&lt;/p&gt;

&lt;p&gt;A process that has created virtual threads stays concurrent. A registered native thread that the collector cannot hold also prevents these stop-the-world cycles. Pausing only some allocating threads is not a valid stop-the-world algorithm. The &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/vm/ByteCodeTranslator/src/cn1_globals.m" rel="noopener noreferrer"&gt;runtime implementation&lt;/a&gt; enforces those boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  More memory can buy fewer collections
&lt;/h2&gt;

&lt;p&gt;The young-generation budget responds to collection duty. Two successive minors consuming more than 5% of their intervals can grow it; below 1%, it can shrink. Growth is bounded, including by a fraction of host memory. Requiring two busy minors avoids treating one collection delayed by the OS scheduler as proof that the application needs a larger heap.&lt;/p&gt;

&lt;p&gt;A larger young generation lets more short-lived objects die before the next collection. That can save CPU. It also leaves more memory resident, and increases the amount of work a later phase may inherit. You have to measure both sides.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-budget.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-09%2Fgc-budget.svg" alt="Conceptual relationship between collection frequency, memory budget and later tracing cost" width="720" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual diagram, not benchmark data. Increasing a budget can reduce frequent collection work while increasing retained memory and the cost of a later scan. The actual curves depend on survival and reference patterns.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One failed transition made this concrete. Deferring the exit decision until the next cycle handed as much as 128 MB of young objects to a concurrent collection. Its fresh-object protection retained them, while application threads kept allocating behind it. The engineering record reports a roughly 500 ms cycle and a page-heap count rising from 3,446 to 10,538 in the affected stress test. The exit decision now runs at the end of the stopped cycle, before application threads resume.&lt;/p&gt;

&lt;p&gt;Another attempted concurrent major promoted about 6.4 million dead objects into the old generation. The next minor then spent 260 to 305 ms examining the remembered set. Those are failure observations from the &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/vm/CLAUDE.md" rel="noopener noreferrer"&gt;GC engineering record&lt;/a&gt;, not advertised pause bounds for the corrected collector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read time and memory together
&lt;/h2&gt;

&lt;p&gt;The committed calibration change for &lt;a href="https://github.com/codenameone/CodenameOne/blob/6516c1ca47/vm/selfhost/perf-baseline/pr/5940.json" rel="noopener noreferrer"&gt;PR #5940&lt;/a&gt; gives a useful view of the trade. These are historical gate baselines for &lt;code&gt;objectAllocation&lt;/code&gt;, with JDK 25 equal to 1. Lower is better in both columns.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runner&lt;/th&gt;
&lt;th&gt;Time ratio, before → after&lt;/th&gt;
&lt;th&gt;Peak memory ratio, before → after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS ARM64, three-CPU runner&lt;/td&gt;
&lt;td&gt;3.696 → 1.106&lt;/td&gt;
&lt;td&gt;0.481 → 0.937&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux x64, AMD EPYC 7763&lt;/td&gt;
&lt;td&gt;5.011 → 1.649&lt;/td&gt;
&lt;td&gt;0.370 → 0.370&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux ARM64, Neoverse N2&lt;/td&gt;
&lt;td&gt;3.043 → 3.043&lt;/td&gt;
&lt;td&gt;0.239 → 0.378&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows x64, AMD family 25/model 1&lt;/td&gt;
&lt;td&gt;4.932 → 1.559&lt;/td&gt;
&lt;td&gt;0.400 → 0.386&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-ratios.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-09%2Fgc-ratios.svg" alt="Before and after calibration ratios for allocation time and peak memory, with JDK 25 at one" width="944" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The macOS row is the clearest cost: much less allocation time, almost twice the previous memory ratio. Linux ARM64 records more memory with its time baseline unchanged. A value unchanged in the overlay can be a carried-forward baseline; it is not a fresh measurement of zero change. The ratios still show ParparVM slower than JDK 25 on this allocation workload after the improvement.&lt;/p&gt;

&lt;p&gt;These are calibration records, &lt;strong&gt;not a new controlled before/after experiment&lt;/strong&gt;. Most selected rows record one calibration run; the Windows row records three. They have no confidence intervals, and later master baselines include further recalibrations. The &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-allocation-baselines.csv" rel="noopener noreferrer"&gt;data CSV&lt;/a&gt; and &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-baseline-overlay.json" rel="noopener noreferrer"&gt;original overlay&lt;/a&gt; retain that context. Compare within a runner row; do not rank different machines from these ratios.&lt;/p&gt;

&lt;p&gt;The allocation workload is deliberately narrow. It tells us about allocation and collection behavior for that survival pattern, not frame latency, network throughput or every application. A backend using virtual threads also follows the concurrent path, so an allocation microbenchmark's hybrid gain cannot be assigned to that backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make a speed gain part of the next baseline
&lt;/h2&gt;

&lt;p&gt;The performance gate runs paired ParparVM and JDK 25 workloads on Linux x64 and ARM64, Windows x64 and ARM64, and macOS ARM64. It alternates which runtime goes first, verifies output and records median ratios. CPU-specific baselines keep one runner model from silently standing in for another.&lt;/p&gt;

&lt;p&gt;For workload timings, the gate uses the fastest measured repetition inside each process; JVM startup is not charged to the workload. Memory is a process peak: footprint on macOS, RSS on Linux and working set on Windows. Those are related measurements, not identical operating-system counters.&lt;/p&gt;

&lt;p&gt;A change outside the permitted tolerance fails in either direction. A large improvement needs a reviewed baseline update too, otherwise a later regression could give it back while remaining below the old limit. Each PR owns an overlay, records a reason for an intentional change and names the value it replaces. Merged overlays are folded without changing the resolved baseline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run locally on the appropriate platform and inspect both metrics.&lt;/span&gt;
vm/selfhost/ci-perf-gate.sh run macos-arm64 /tmp/cn1-perf
vm/selfhost/ci-perf-gate.sh verdict /tmp/cn1-perf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This comparative gate covers the five host OS/architecture combinations above. It is not a JDK comparison running on an iPhone, Android device or browser. Those ports have their own behavior and screenshot coverage. The &lt;a href="https://www.codenameone.com/port-status/" rel="noopener noreferrer"&gt;port status page&lt;/a&gt; exposes the distinction, and &lt;a href="https://github.com/codenameone/CodenameOne/pull/5930" rel="noopener noreferrer"&gt;PR #5930&lt;/a&gt; explains the baseline workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the gain you measured
&lt;/h2&gt;

&lt;p&gt;PGO and adaptive collection improve different parts of execution. Compare a profile-guided build with a regular build using the same application workflow, and keep the time and memory results together. For compiler and runtime changes, the performance gate gives the next release a baseline to preserve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which part of your app would benefit most from a profile-guided build: startup, rendering, or repeated background work?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
