Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started

This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.

The paint thread

This is probably the most important rule of the framework for not corrupting your app.

CrossFly is single-threaded on the paint thread

Every UI and rendering operation happens on a single thread — the paint thread. Main calls create(), render(), and resize() always on it; every onShow, onPaint, onLayout, touch callback, and component change runs on it.

Why this decision? Simplicity and correctness. With a single thread of execution touching the UI, there are no locks, no contended shared state on the painting path. The price is a rule you must respect: never touch the UI from another thread.

The canonical pattern: heavy work off the thread, UI back with onUI

Network, database, file, long computation → background thread. To touch the UI with the result, go back to the paint thread with Component.onUI(...):

new Thread(() -> {
   List<Cliente> dados = repositorio.buscarTudo();   // heavy work, background thread
   Component.onUI(() -> {                             // back to the paint thread
      lista.setItems(dados);                          // now it's safe to touch the UI
      Component.repaint();                             // requests a new frame
   });
}).start();

There is also Main.runOnPaintThread(Runnable) (and the variant runOnPaintThreadAndWait(...)) for the same purpose outside the context of a Component.

Classic symptom of violating the rule: the app crashes or behaves erratically when “updating the screen” from inside a network/timer callback. The fix is always to wrap the UI touch with onUI(...).

On-demand render

The paint thread does not keep redrawing continuously. A new frame is only generated when someone calls repaint() (internally Gdx.graphics.requestRendering()). This saves battery — an idle screen doesn’t consume GPU.

Most component setters already call repaint() for you. The consequences to remember:

  • Changed something the framework doesn’t know about? Call repaint() yourself.
  • Animation? It needs to keep the render request alive on each frame (this is what the animators do in onUpdate; see Value animations).

onUI

repaint

background thread
(network/DB/compute)

paint thread

requestRendering

next frame
(paints the tree)

See also

Was this page helpful?