Skip to content

Commit 29e40ec

Browse files
authored
在 DownloadListPage 中添加图标缓存 (#4866)
1 parent 4977896 commit 29e40ec

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,14 @@ public static Image newBuiltinImage(String url, double requestedWidth, double re
11881188

11891189
public static Task<Image> getRemoteImageTask(String url, int requestedWidth, int requestedHeight, boolean preserveRatio, boolean smooth) {
11901190
return new CacheFileTask(url)
1191-
.thenApplyAsync(file -> loadImage(file, requestedWidth, requestedHeight, preserveRatio, smooth));
1191+
.thenApplyAsync(file -> loadImage(file, requestedWidth, requestedHeight, preserveRatio, smooth))
1192+
.setSignificance(Task.TaskSignificance.MINOR);
1193+
}
1194+
1195+
public static Task<Image> getRemoteImageTask(URI uri, int requestedWidth, int requestedHeight, boolean preserveRatio, boolean smooth) {
1196+
return new CacheFileTask(uri)
1197+
.thenApplyAsync(file -> loadImage(file, requestedWidth, requestedHeight, preserveRatio, smooth))
1198+
.setSignificance(Task.TaskSignificance.MINOR);
11921199
}
11931200

11941201
public static ObservableValue<Image> newRemoteImage(String url, int requestedWidth, int requestedHeight, boolean preserveRatio, boolean smooth) {

HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import javafx.scene.control.Label;
3636
import javafx.scene.control.Skin;
3737
import javafx.scene.control.SkinBase;
38+
import javafx.scene.image.Image;
3839
import javafx.scene.image.ImageView;
3940
import javafx.scene.input.KeyCode;
4041
import javafx.scene.input.KeyEvent;
@@ -60,16 +61,24 @@
6061
import org.jackhuang.hmcl.util.Lang;
6162
import org.jackhuang.hmcl.util.StringUtils;
6263
import org.jackhuang.hmcl.util.i18n.I18n;
64+
import org.jackhuang.hmcl.util.io.NetworkUtils;
6365
import org.jackhuang.hmcl.util.javafx.BindingMapping;
6466
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
67+
import org.jetbrains.annotations.NotNull;
6568

69+
import java.lang.ref.WeakReference;
70+
import java.net.URI;
6671
import java.util.*;
72+
import java.util.concurrent.CancellationException;
73+
import java.util.concurrent.CompletableFuture;
74+
import java.util.concurrent.CompletionException;
6775
import java.util.stream.Collectors;
6876

6977
import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
7078
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
7179
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
7280
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor;
81+
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
7382

7483
public class DownloadListPage extends Control implements DecoratorPage, VersionPage.VersionLoadable {
7584
protected final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>();
@@ -523,6 +532,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) {
523532

524533
// ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here
525534
ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE);
535+
var iconCache = new WeakHashMap<String, WeakReference<CompletableFuture<Image>>>();
526536
listView.setCellFactory(x -> new FloatListCell<>(listView) {
527537
private final TwoLineListItem content = new TwoLineListItem();
528538
private final ImageView imageView = new ImageView();
@@ -549,8 +559,64 @@ protected void updateControl(RemoteMod dataItem, boolean empty) {
549559
dataItem.getCategories().stream()
550560
.map(category -> getSkinnable().getLocalizedCategory(category))
551561
.forEach(content::addTag);
552-
if (StringUtils.isNotBlank(dataItem.getIconUrl())) {
553-
imageView.imageProperty().bind(FXUtils.newRemoteImage(dataItem.getIconUrl(), 80, 80, true, true));
562+
loadIcon(dataItem);
563+
}
564+
565+
private void loadIcon(RemoteMod mod) {
566+
if (StringUtils.isBlank(mod.getIconUrl())) {
567+
imageView.setImage(null);
568+
return;
569+
}
570+
571+
WeakReference<CompletableFuture<Image>> cacheRef = iconCache.get(mod.getIconUrl());
572+
CompletableFuture<Image> cache;
573+
if (cacheRef != null && (cache = cacheRef.get()) != null) {
574+
loadIcon(cache, mod.getIconUrl());
575+
return;
576+
}
577+
578+
URI iconUrl = NetworkUtils.toURIOrNull(mod.getIconUrl());
579+
if (iconUrl == null) {
580+
imageView.setImage(null);
581+
return;
582+
}
583+
584+
CompletableFuture<Image> future = new CompletableFuture<>();
585+
WeakReference<CompletableFuture<Image>> futureRef = new WeakReference<>(future);
586+
iconCache.put(mod.getIconUrl(), futureRef);
587+
588+
FXUtils.getRemoteImageTask(iconUrl, 80, 80, true, true)
589+
.whenComplete(Schedulers.defaultScheduler(), (result, exception) -> {
590+
if (exception == null) {
591+
future.complete(result);
592+
} else {
593+
LOG.warning("Failed to load image from " + iconUrl, exception);
594+
future.completeExceptionally(exception);
595+
}
596+
}).start();
597+
loadIcon(future, mod.getIconUrl());
598+
}
599+
600+
private void loadIcon(@NotNull CompletableFuture<Image> future,
601+
@NotNull String iconUrl) {
602+
Image image;
603+
try {
604+
image = future.getNow(null);
605+
} catch (CancellationException | CompletionException ignored) {
606+
imageView.setImage(null);
607+
return;
608+
}
609+
610+
if (image != null) {
611+
imageView.setImage(image);
612+
} else {
613+
imageView.setImage(null);
614+
future.thenAcceptAsync(result -> {
615+
RemoteMod item = getItem();
616+
if (item != null && iconUrl.equals(item.getIconUrl())) {
617+
this.imageView.setImage(result);
618+
}
619+
}, Schedulers.javafx());
554620
}
555621
}
556622
});

HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.jackhuang.hmcl.util.Pair;
2121
import org.jackhuang.hmcl.util.StringUtils;
2222
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
2324

2425
import java.io.*;
2526
import java.net.*;
@@ -419,6 +420,15 @@ public static String decodeURL(String toDecode) {
419420
public static @NotNull URI toURI(@NotNull URL url) {
420421
return toURI(url.toExternalForm());
421422
}
422-
// ====
423423

424+
public static @Nullable URI toURIOrNull(String uri) {
425+
if (StringUtils.isNotBlank(uri)) {
426+
try {
427+
return toURI(uri);
428+
} catch (Exception ignored) {
429+
}
430+
}
431+
432+
return null;
433+
}
424434
}

0 commit comments

Comments
 (0)