跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用

本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。

如何在 paint 线程之外访问数据库

目的。 繁重的查询(扫描、批量操作、迁移)如果在 CrossFly 唯一的 paint 线程上运行,会导致 UI 卡死。规则是:在工作线程中执行数据库 I/O,并且仅通过 Component.onUI(...) 更新 UI,它会将代码重新转发到 UI 线程。

使用的 API

  • new Thread(Runnable).start() —— 创建一个工作线程用于访问数据库。所有 SqliteDb/query/bulkInsert 都在这里运行,远离 paint 线程。
  • Component.onUI(Runnable r) —— 将 r 排入队列,使其在 UI 线程上运行(内部为 Gdx.app.postRunnable)。所有对组件的操作(设置文本、向列表添加项、repaint)都必须在此回调内发生。
  • SqliteDb / SqliteCursor —— 在其自身线程内打开并关闭(通过 try-with-resources)。不要在线程之间共享连接;在你使用的地方打开你自己的连接。

线程处理黄金法则。 读/写数据库:工作线程。触碰 UI 组件:Component.onUI。绝不能反过来——在 UI 线程之外访问组件会造成损坏;用 I/O 阻塞 paint 线程会使应用卡死。

离开线程前先收集。 SqliteCursor 是 AutoCloseable 且与其连接绑定。不要把 cursor 传入 onUI。在工作线程中将数据物化(例如放入一个 List),并只把准备好的列表传给 UI。

完整示例(复制、粘贴并运行)

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

/** 可执行的主界面。将 launcher 指向此类(classname)并运行:gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      new Thread(() ->
      {
         // --- 工作 thread:数据库 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));   // 在离开 thread 之前采集
         }

         // --- 仅为更新组件而回到 UI ---
         Component.onUI(() ->
         {
            // 此处可安全操作组件(设置列表、repaint 等)
            preencheLista(nomes);
         });
      }).start();
   }

   /** Stub:在此用加载的数据更新你的 UI 组件。 */
   private void preencheLista(List<String> nomes)
   {
      // 例如:在列表中构建项、设置文本、repaint()...
   }
}

下面的代码片段仅展示核心部分。要运行它,请将其放入上面的 MainWindow 骨架中。

最小片段

// 已在源码核实:Component.onUI (Gdx.app.postRunnable),SqliteDb.query/cursor 从 0 开始
import dev.crossfly.sqlite.*;
import dev.crossfly.ui.Component;
import java.util.*;

new Thread(() ->
{
   // --- 工作 thread:数据库 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));   // 在离开 thread 之前采集
   }

   // --- 仅为更新组件而回到 UI ---
   Component.onUI(() ->
   {
      // 此处可安全操作组件(设置列表、repaint 等)
      preencheLista(nomes);
   });
}).start();

深入了解

这个页面有帮助吗?