From 4a0831a1552ba03bd495720de8e3a47f857b7e17 Mon Sep 17 00:00:00 2001 From: yushijinhun Date: Sat, 22 Sep 2018 14:04:05 +0800 Subject: [PATCH] Replace Properties with InvocationDispatcher & Make config saving async --- .../java/org/jackhuang/hmcl/Launcher.java | 3 - .../jackhuang/hmcl/setting/ConfigHolder.java | 34 ++++++------ .../java/org/jackhuang/hmcl/task/Task.java | 11 ++-- .../org/jackhuang/hmcl/util/Constants.java | 20 ------- .../hmcl/util/InvocationDispatcher.java | 55 +++++++++++++++++++ .../hmcl/util/javafx/Properties.java | 40 -------------- 6 files changed, 77 insertions(+), 86 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/javafx/Properties.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index c3f2c65f1..5aa66fc8e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -80,9 +80,6 @@ public final class Launcher extends Application { try { Logging.start(LOG_DIRECTORY); - // NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher"); - Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER; - LOG.info("*** " + Metadata.TITLE + " ***"); LOG.info("Operating System: " + System.getProperty("os.name") + ' ' + OperatingSystem.SYSTEM_VERSION); LOG.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java index e49b142ca..f1b11831f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java @@ -27,9 +27,11 @@ import java.nio.file.Paths; import java.util.Map; import java.util.logging.Level; +import org.jackhuang.hmcl.util.InvocationDispatcher; import org.jackhuang.hmcl.util.platform.OperatingSystem; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.Logging.LOG; public final class ConfigHolder { @@ -43,6 +45,19 @@ public final class ConfigHolder { private static Config configInstance; private static boolean newlyCreated; + private static InvocationDispatcher configWriter = new InvocationDispatcher<>(content -> { + thread(() -> { + LOG.info("Saving config"); + try { + synchronized (configLocation) { + Files.write(configLocation, content.get().getBytes(UTF_8)); + } + } catch (IOException e) { + LOG.log(Level.SEVERE, "Failed to save config", e); + } + }); + }); + public static Config config() { if (configInstance == null) { throw new IllegalStateException("Configuration hasn't been loaded"); @@ -69,7 +84,7 @@ public final class ConfigHolder { Settings.init(); if (newlyCreated) { - saveConfig(); + markConfigDirty(); // hide the config file on windows if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { @@ -123,22 +138,7 @@ public final class ConfigHolder { return new Config(); } - private static void saveConfig() throws IOException { - LOG.info("Saving config"); - try { - Files.write(configLocation, configInstance.toJson().getBytes(UTF_8)); - } catch (IOException e) { - LOG.log(Level.SEVERE, "Failed to save config", e); - throw e; - } - } - static void markConfigDirty() { - // TODO: change this to async - try { - saveConfig(); - } catch (IOException ignored) { - // ignore it as it has been logged - } + configWriter.accept(configInstance.toJson()); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index e717a1ffa..5ad4639fe 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.task; +import javafx.application.Platform; import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyStringProperty; @@ -26,12 +27,10 @@ import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalRunnable; -import org.jackhuang.hmcl.util.javafx.Properties; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.logging.Level; @@ -176,8 +175,8 @@ public abstract class Task { } private long lastTime = Long.MIN_VALUE; - private final AtomicReference progressUpdate = new AtomicReference<>(); private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", -1); + private final InvocationDispatcher progressUpdate = InvocationDispatcher.runOn(Platform::runLater, progress::set); public ReadOnlyDoubleProperty progressProperty() { return progress.getReadOnlyProperty(); @@ -198,18 +197,18 @@ public abstract class Task { } protected void updateProgressImmediately(double progress) { - Properties.updateAsync(this.progress, progress, progressUpdate); + progressUpdate.accept(progress); } - private final AtomicReference messageUpdate = new AtomicReference<>(); private final ReadOnlyStringWrapper message = new ReadOnlyStringWrapper(this, "message", null); + private final InvocationDispatcher messageUpdate = InvocationDispatcher.runOn(Platform::runLater, message::set); public final ReadOnlyStringProperty messageProperty() { return message.getReadOnlyProperty(); } protected final void updateMessage(String newMessage) { - Properties.updateAsync(message, newMessage, messageUpdate); + messageUpdate.accept(newMessage); } public final void run() throws Exception { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Constants.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Constants.java index 7d8a5e6b8..1948ab633 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Constants.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/Constants.java @@ -17,10 +17,6 @@ */ package org.jackhuang.hmcl.util; -import org.jackhuang.hmcl.task.Schedulers; -import java.awt.*; -import java.util.function.Consumer; - /** * Constants. * @@ -34,20 +30,4 @@ public final class Constants { public static final String DEFAULT_LIBRARY_URL = "https://libraries.minecraft.net/"; public static final String DEFAULT_VERSION_DOWNLOAD_URL = "https://s3.amazonaws.com/Minecraft.Download/versions/"; public static final String DEFAULT_INDEX_URL = "https://s3.amazonaws.com/Minecraft.Download/indexes/"; - - public static Consumer UI_THREAD_SCHEDULER = s -> Schedulers.computation().schedule(s::run); - - public static final Consumer SWING_UI_THREAD_SCHEDULER = s -> { - if (EventQueue.isDispatchThread()) - s.run(); - else - EventQueue.invokeLater(s); - }; - - public static final Consumer JAVAFX_UI_THREAD_SCHEDULER = s -> { - if (javafx.application.Platform.isFxApplicationThread()) - s.run(); - else - javafx.application.Platform.runLater(s); - }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java new file mode 100644 index 000000000..f80dfbf02 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java @@ -0,0 +1,55 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2018 huangyuhui + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see {http://www.gnu.org/licenses/}. + */ + +package org.jackhuang.hmcl.util; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * When {@link #accept(ARG)} is called, this class invokes the handler on another thread. + * If {@link #accept(ARG)} is called more than one time before the handler starts processing, + * the handler will only be invoked once, taking the latest argument as its input. + * + * @author yushijinhun + */ +public class InvocationDispatcher implements Consumer { + + public static InvocationDispatcher runOn(Consumer executor, Consumer action) { + return new InvocationDispatcher<>(arg -> executor.accept(() -> { + action.accept(arg.get()); + })); + } + + private Consumer> handler; + + private AtomicReference> pendingArg = new AtomicReference<>(); + + public InvocationDispatcher(Consumer> handler) { + this.handler = handler; + } + + @Override + public void accept(ARG arg) { + if (pendingArg.getAndSet(Optional.ofNullable(arg)) == null) { + handler.accept(() -> pendingArg.getAndSet(null).orElse(null)); + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/javafx/Properties.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/javafx/Properties.java deleted file mode 100644 index ee94d8b17..000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/javafx/Properties.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Hello Minecraft! Launcher. - * Copyright (C) 2018 huangyuhui - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see {http://www.gnu.org/licenses/}. - */ -package org.jackhuang.hmcl.util.javafx; - -import javafx.beans.property.Property; - -import java.util.concurrent.atomic.AtomicReference; - -import org.jackhuang.hmcl.util.Constants; - -/** - * - * @author huangyuhui - */ -public final class Properties { - - private Properties() { - } - - public static void updateAsync(Property property, T newValue, AtomicReference update) { - if (update.getAndSet(newValue) == null) - Constants.UI_THREAD_SCHEDULER.accept(() -> - property.setValue(update.getAndSet(null))); - } -}