9.0 KiB
JavaRuntimeService
Обзор класса
JavaRuntimeService — каталог сборок Java, которые лаунчер умеет скачать: качает, кэширует в
папке лаунчера и отдаёт из кэша, пока тот не устарел. Устроен так же, как
VersionManifestService.
Источников два, и они дополняют друг друга. Mojang (категория «Java») — ровно тот рантайм, которым запускает игру официальный лаунчер: версий немного, зато они заведомо совместимы. Eclipse Temurin (категории JDK и JRE) — свежие сборки всех мажорных версий, включая те, до которых Mojang ещё не дошёл.
Отдаются только сборки под текущие операционную систему и архитектуру: выбрать заведомо
неработающую нечем. Исключение — macOS на Apple Silicon, где под Java 8 и 16 сборок aarch64 нет
вовсе и приходится брать x64, который работает через Rosetta.
Место в проекте и зависимости
Подключает javaruntime.h: перечисление JavaRuntimeKind и структура
JavaRuntimeEntry приходят оттуда.
Экземпляр создаётся и принадлежит LauncherBackend. Записями каталога пользуется JavaInstaller.
Путь к файлу кэша даёт LauncherPaths::javaCatalogFile() из launcherpaths.h.
Требования сборки: Qt6::Core (QDateTime, QHash, QSet) и Qt6::Network
(QNetworkAccessManager).
Иерархия и роль
Наследует QObject: мета-объектная система, два сигнала и владение по родителю. Виртуальных
методов базового класса не переопределяет.
Псевдонимы типов
JavaRuntimeService::Callback — std::function<void(bool ok, const QString &warning)>. Как и в
остальных каталогах лаунчера, ok == true с непустым warning означает данные из устаревшего
кэша.
Публичные методы
explicit JavaRuntimeService(QObject *parent = nullptr)
Создаёт сервис и его QNetworkAccessManager. Кэш читается лениво. Конструктор помечен explicit.
void ensureLoaded(Callback callback, bool forceRefresh = false)
Единственная точка входа. Свежий кэш означает отложенный вызов колбэка без сети; иначе запускается
обновление. Параметр forceRefresh обходит проверку свежести — так работает кнопка обновления
каталога.
Одно обновление складывается из нескольких запросов сразу к обоим источникам. Ответы приходят вразнобой, поэтому они накапливаются, и каталог подменяется целиком только когда пришли все.
QList<JavaRuntimeEntry> entries(JavaRuntimeKind kind) const
Сборки одной категории: Mojang, Jdk или Jre. Новые версии идут первыми.
std::optional<JavaRuntimeEntry> find(const QString &id) const
Запись по идентификатору сборки; std::nullopt, если такой нет. Поиск идёт по внутреннему
указателю.
std::optional<JavaRuntimeEntry> bestFor(int major) const
Что скачать, если для версии игры нужна Java указанной мажорной версии, а подходящей в системе нет.
Правила выбора: точное совпадение мажорной версии предпочтительнее более новой, а JDK
предпочтительнее JRE — установщики Forge и NeoForge иногда требуют инструментов из полного
комплекта. std::nullopt означает, что предложить нечего.
bool isRefreshing() const
Идут ли сейчас запросы. Признак остаётся истинным, пока не завершится последний из них.
bool hasData() const
Есть ли в каталоге хоть что-то — из сети или из кэша.
Сигналы
catalogChanged()
Каталог заменён новыми данными. Испускается один раз за обновление, когда пришли ответы от всех источников, а не по каждому из них.
Обработчик перечитывает entries() для нужных категорий и обновляет интерфейс.
refreshingChanged()
Изменился признак обновления. Обработчик показывает или убирает индикатор загрузки.
Владение и время жизни
Класс наследует QObject и принимает parent — родитель его и удалит.
QNetworkAccessManager создаётся в конструкторе с сервисом в роли родителя.
Отложенные колбэки хранятся до завершения текущего обновления. Уничтожение сервиса с незавершёнными запросами обрывает их вместе с менеджером сети, и колбэки не вызываются.
Потокобезопасность
Только поток GUI. Чтение и запись кэша выполняются синхронно в вызывающем потоке.
Взаимодействие с другими классами
LauncherBackend вызывает ensureLoaded() при открытии окна выбора Java и по кнопке обновления,
а сигналы переправляет в свойства, которые читает QML. Перед отдачей в интерфейс он сводит записи
каталога с уже установленными сборками из JavaRuntimeStore — окно выбора показывает статус, не
считая ничего само.
JavaInstaller получает запись каталога и по ней скачивает и распаковывает
сборку. bestFor() используется, когда для запуска не хватает Java и лаунчер должен сам
предложить, что поставить.
Требования версий игры к Java берутся из пространства имён JavaRequirement в
javaruntime.h.
Внешнее взаимодействие
Сеть, исходящие запросы. Класс обращается к двум внешним каталогам: API Adoptium (сборки Temurin) и списку рантаймов Mojang. Формат обоих — JSON поверх HTTPS, разбор разделён на две функции.
Запросов на одно обновление несколько: список сборок Temurin запрашивается по мажорным версиям,
и на macOS с Apple Silicon неудачный запрос сборки aarch64 может быть переспрошен для x64 —
это и есть тот самый случай Java 8 и 16.
Общий счётчик незавершённых запросов сводит их воедино: пока он не обнулился, каталог не подменяется, а предупреждения от отдельных источников накапливаются.
Все сигналы и колбэки приходят в поток GUI.
Пример использования
auto *javaCatalog = new JavaRuntimeService(this);
connect(javaCatalog, &JavaRuntimeService::catalogChanged, this, &Backend::rebuildJavaCatalog);
javaCatalog->ensureLoaded([this, javaCatalog](bool ok, const QString &warning) {
if (!ok) {
emit javaCatalogError(warning);
return;
}
const auto best = javaCatalog->bestFor(version.javaMajor);
if (best)
emit suggestJavaInstall(best->id, best->version);
});
При создании этого документа использовался ИИ.