This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.
How to access the database outside the paint thread
Purpose. Heavy queries (scans, bulk, migrations) freeze the UI if they run on CrossFly’s single paint thread. The rule is: do the database I/O on a worker thread and update the UI only via Component.onUI(...), which reroutes the code to the UI thread.
APIs used
new Thread(Runnable).start()— creates a worker thread for database access. AllSqliteDb/query/bulkInsertruns here, away from the paint thread.Component.onUI(Runnable r)— queuesrto run on the UI thread (internallyGdx.app.postRunnable). All component manipulation (setting text, adding items to the list,repaint) must happen inside this callback.SqliteDb/SqliteCursor— opened and closed within the thread itself (viatry-with-resources). Do not share a connection between threads; open your own where you’re going to use it.
Golden threading rule. Read/write the database: worker thread. Touch UI components:
Component.onUI. Never the other way around — accessing components off the UI thread causes corruption; blocking the paint thread with I/O freezes the app.
Collect before leaving the thread. The
SqliteCursorisAutoCloseableand tied to its connection. Do not pass the cursor intoonUI. Materialize the data (e.g., into aList) on the worker thread and pass only the finished list to the UI.
Full example (copy, paste and run)
package com.exemplo;
import java.util.*; // List, ArrayList
import dev.crossfly.ui.*; // Window, Component
import dev.crossfly.ui.layout.*; // VerticalLayout
import dev.crossfly.sqlite.*; // SqliteDb, SqliteCursor
/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
@Override
public void onShow() throws Exception
{
super.onShow();
setLayout(new VerticalLayout());
new Thread(() ->
{
// --- worker thread: database I/O ---
List<String> nomes = new ArrayList<>();
try (SqliteDb db = SqliteDb.open("app.db");
SqliteCursor c = db.query("SELECT name FROM person ORDER BY name"))
{
while (c.next())
nomes.add(c.getString(0)); // collects BEFORE leaving the thread
}
// --- back to the UI only to update components ---
Component.onUI(() ->
{
// here it's safe to touch components (set list, repaint, etc.)
preencheLista(nomes);
});
}).start();
}
/** Stub: update your UI components here with the loaded data. */
private void preencheLista(List<String> nomes)
{
// e.g. build items in a list, set texts, repaint()...
}
}
The snippet below shows only the core. To run it, place it inside the
MainWindowskeleton above.
Minimal snippet
// verified in the source: Component.onUI (Gdx.app.postRunnable), SqliteDb.query/cursor 0-based
import dev.crossfly.sqlite.*;
import dev.crossfly.ui.Component;
import java.util.*;
new Thread(() ->
{
// --- worker thread: database I/O ---
List<String> nomes = new ArrayList<>();
try (SqliteDb db = SqliteDb.open("app.db");
SqliteCursor c = db.query("SELECT name FROM person ORDER BY name"))
{
while (c.next())
nomes.add(c.getString(0)); // collects BEFORE leaving the thread
}
// --- back to the UI only to update components ---
Component.onUI(() ->
{
// here it's safe to touch components (set list, repaint, etc.)
preencheLista(nomes);
});
}).start();
Dig deeper
- Background work · Error handling and logging
- Sibling recipes: Parameterized CRUD · Transaction and bulk writing