Files
2026-09-03 09:16:56 +03:00

11 KiB
Raw Permalink Blame History

JavaInstaller

Обзор класса

JavaInstaller ставит сборку Java в <root>/java/<id>. Как и ModLoaderInstaller, он прячет за одним фасадом два разных пути.

Temurin отдаёт один архив: лаунчер качает его, сверяет sha256 и распаковывает. Mojang отдаёт манифест с деревом файлов: лаунчер качает файлы по отдельности, как это делает официальный лаунчер.

Набор геттеров прогресса повторяет VersionInstaller: панель загрузки в интерфейсе читает их одинаково, независимо от того, кто сейчас работает.

Место в проекте и зависимости

Подключает javaruntime.h: на вход установщик принимает запись каталога JavaRuntimeEntry, а результат записывает через JavaRuntimeStore.

Экземпляр создаётся и принадлежит LauncherBackend; записи каталога приходят от JavaRuntimeService.

Требования сборки: Qt6::Core (QCryptographicHash, QSaveFile, QProcess, QTimer), Qt6::CorePrivate (QZipReader для распаковки zip) и Qt6::Network.

Иерархия и роль

Наследует QObject: мета-объектная система, пять сигналов и владение по родителю. Объявлен виртуальный деструктор — класс владеет незавершёнными загрузками, открытым архивом и процессом распаковки.

Публичные структуры

JavaFileTask

Один файл рантайма Mojang.

Поле Тип По умолчанию Описание
url QUrl Откуда качать
path QString Абсолютный путь назначения
sha1 QString Контрольная сумма файла
size qint64 0 Размер в байтах
executable bool false Файлу нужно выставить право на исполнение — иначе bin/java не запустится
attempts int 0 Сколько попыток уже сделано

JavaActiveDownload

Файл в процессе скачивания: задача, сетевой ответ, открытый QSaveFile, накапливаемая контрольная сумма и число принятых байт. Файлы пишутся потоком — рантайм весит около двухсот мегабайт.

Структура ZipExtraction, хранящая состояние распаковки, объявлена вперёд и спрятана в .cpp: так приватный заголовок QZipReader не расходится по проекту вместе с этим заголовком.

Публичные методы

explicit JavaInstaller(QObject *parent = nullptr)

Создаёт установщик, его QNetworkAccessManager и два таймера. Конструктор помечен explicit.

bool isRunning() const

Идёт ли установка прямо сейчас.

QString runtimeId() const

Идентификатор устанавливаемой сборки.

QString label() const

Подпись установки для интерфейса.

QString stage() const

Текущий этап словами: загрузка, проверка, распаковка.

QString currentFile() const

Файл, который обрабатывается сейчас.

qint64 bytesDone() const

Сколько байт уже получено. Байты считаются только на загрузке: на распаковке считать нечего, и панель по нулевому итогу сама прячет мегабайты.

qint64 bytesTotal() const

Ожидаемый общий объём загрузки.

double fraction() const

Доля выполнения от 0 до 1 либо -1, пока итог неизвестен. На этапе распаковки доля считается по числу обработанных записей архива или файлов дерева, а не по байтам.

void install(const JavaRuntimeEntry &entry)

Ставит сборку по записи каталога. Путь установки выбирается по полю archive записи: значения zip и tar.gz ведут по пути Temurin, значение mojang — по пути манифеста.

По завершении установщик находит исполняемый файл java в распакованном дереве через JavaRuntimeStore::locateBinary() и записывает описание сборки рядом с ней.

Одновременно ставится одна сборка; очереди у этого установщика нет.

void cancel()

Отменяет установку. Отмена проверяется между файлами и между кусками распаковки, поэтому срабатывает не мгновенно, но без замораживания интерфейса.

Сигналы

started(const QString &label)

Установка началась. Обработчик показывает панель прогресса.

progressChanged()

Изменились числа прогресса; испускается не чаще, чем позволяет внутренний таймер. Обработчик перечитывает геттеры.

finished(const QString &runtimeId, const QString &javaPath)

Сборка установлена; во втором параметре — абсолютный путь к исполняемому файлу java.

Обработчик обновляет каталог: сборка становится помеченной как скачанная, а диалог настроек перечитывает её описание.

failed(const QString &label, const QString &message)

Установка не удалась; в параметре — текст ошибки для пользователя.

canceled(const QString &label)

Установка отменена пользователем.

Владение и время жизни

Класс наследует QObject и принимает parent — родитель его и удалит. QNetworkAccessManager и оба таймера создаются в конструкторе с установщиком в роли родителя.

Владение внутренними ресурсами построено на RAII: скачиваемый архив хранится как std::unique_ptr<QSaveFile>, состояние распаковки — как std::unique_ptr<ZipExtraction>, активные загрузки — как std::shared_ptr<JavaActiveDownload> с собственным QSaveFile внутри. Незавершённая запись отменяется вместе с уничтожением объекта, и испорченный файл не попадает на место назначения.

Процесс tar, используемый для распаковки архивов tar.gz, создаётся по ходу работы и завершается в деструкторе.

Потокобезопасность

Только поток GUI. Отдельного потока у класса нет намеренно: распаковка zip идёт по кускам по таймеру — держать поток GUI занятым на всю сотню мегабайт нельзя, а заводить поток ради одной операции незачем. Распаковка tar.gz отдана внешнему процессу, который работает параллельно сам.

Взаимодействие с другими классами

LauncherBackend вызывает install() с записью, полученной от JavaRuntimeService, и переправляет сигналы прогресса в те же свойства, что и остальные установщики. Сигнал finished бэкенд переправляет в QML под собственным именем — на него подписан диалог настроек, чтобы обновить строку выбранной сборки, когда та докачается.

Раскладку папки <root>/java, поиск исполняемого файла и запись описания обеспечивает JavaRuntimeStore из javaruntime.h.

Внешнее взаимодействие

Сеть, исходящие запросы. Загрузка по HTTPS с серверов Adoptium или Mojang. Архив Temurin скачивается одним запросом с проверкой sha256; дерево Mojang — множеством параллельных запросов, каждый с проверкой sha1. Неудачная задача повторяется, счётчик попыток хранится в самой задаче.

Дочерний процесс. Архивы tar.gz распаковываются системным tar через QProcess — собственного распаковщика для этого формата в Qt нет. Обмен односторонний: лаунчер запускает процесс и ждёт его завершения.

Файловая система. После распаковки дерева Mojang применяются символические ссылки из манифеста, а файлам с признаком executable выставляется право на исполнение.

Все сигналы приходят в поток GUI.

Пример использования

auto *javaInstaller = new JavaInstaller(this);

connect(javaInstaller, &JavaInstaller::finished, this,
        [this](const QString &runtimeId, const QString &javaPath) {
            m_settings.javaRuntime = runtimeId;
            m_resolvedJavaPath = javaPath;
            emit javaRuntimeInstalled(runtimeId);
        });

const auto entry = javaCatalog->find(runtimeId);
if (entry)
    javaInstaller->install(*entry);

При создании этого документа использовался ИИ.