Fix editor idle memory leak

This commit is contained in:
fgnm
2024-05-01 11:46:30 +02:00
parent 37460c76ac
commit 2f3b9049a1
8 changed files with 84 additions and 39 deletions
Binary file not shown.
+1 -1
View File
@@ -140,7 +140,7 @@ dependencies {
implementation "com.github.lyze237:gdx-TinyVG:$gdxTinyVGVersion"
implementation "com.mortennobel:java-image-scaling:0.8.6"
implementation "org.apache.commons:commons-lang3:3.12.0"
implementation "commons-io:commons-io:2.7"
implementation "commons-io:commons-io:2.16.1"
implementation 'net.mountainblade:modular:1.0'
testImplementation group: 'junit', name: 'junit', version: '4.12'
+3 -1
View File
@@ -5,8 +5,10 @@ plugins {
group 'games.rednblack'
repositories {
mavenLocal()
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url 'https://jitpack.io' }
}
dependencies {
@@ -21,7 +23,7 @@ dependencies {
implementation project(":hyperlap2d-common-api")
implementation project(":hyperlap2d-runtime-libgdx")
implementation "org.apache.commons:commons-io:1.3.2"
implementation "commons-io:commons-io:2.16.1"
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
@@ -44,14 +44,9 @@ import games.rednblack.h2d.common.ProgressHandler;
import games.rednblack.h2d.common.vo.ProjectVO;
import games.rednblack.h2d.common.vo.SceneConfigVO;
import games.rednblack.h2d.common.vo.TexturePackerVO;
import games.rednblack.puremvc.Facade;
import games.rednblack.puremvc.Proxy;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
import javax.imageio.ImageIO;
@@ -59,10 +54,14 @@ import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ProjectManager extends Proxy {
private static final String TAG = ProjectManager.class.getCanonicalName();
@@ -93,7 +92,7 @@ public class ProjectManager extends Proxy {
private ProjectExportSettings projectExportSettings;
private LivePreviewSettings livePreviewSettings;
private FileAlterationMonitor fileWatcherMonitor;
private Thread fileWatcherThread;
@Override
public void onRegister() {
@@ -241,43 +240,84 @@ public class ProjectManager extends Proxy {
}
}
private void addFileWatcher(String projectPath) throws Exception {
private void addFileWatcher(String projectPath) {
stopFileWatcher();
fileWatcherMonitor = new FileAlterationMonitor(2000);
if (fileWatcherThread != null) return;
FileAlterationObserver observer = new FileAlterationObserver(projectPath);
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
Path directory = Paths.get(projectPath);
fileWatcherThread = new Thread(new Runnable() {
@Override
public void onFileCreate(File file) {
Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_CREATED, file));
}
public void run() {
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
registerAll(directory, watchService);
@Override
public void onFileDelete(File file) {
Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_DELETED, file));
}
while (true) {
WatchKey key;
try {
// Poll for file system events
key = watchService.poll(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return;
}
@Override
public void onFileChange(File file) {
Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_MODIFIED, file));
}
};
observer.addListener(listener);
if (key == null) {
// No events within the timeout period
continue;
}
fileWatcherMonitor.addObserver(observer);
fileWatcherMonitor.start();
List<WatchEvent<?>> events = key.pollEvents();
for (int i = 0; i < events.size(); i++) {
WatchEvent<?> event = events.get(i);
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
Path filePath = ((Path) key.watchable()).resolve(fileName);
File file = filePath.toFile();
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_CREATED, file));
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_DELETED, file));
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
Gdx.app.postRunnable(() -> facade.sendNotification(MsgAPI.PROJECT_FILE_MODIFIED, file));
}
}
// Reset the key
boolean valid = key.reset();
if (!valid) {
break; // Exit the loop if the key is no longer valid
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, "FileWatcherThread");
fileWatcherThread.setDaemon(true);
fileWatcherThread.start();
}
public void stopFileWatcher() {
if (fileWatcherMonitor != null) {
try {
fileWatcherMonitor.stop();
} catch (Exception e) {
e.printStackTrace();
if (fileWatcherThread == null) return;
fileWatcherThread.interrupt();
fileWatcherThread = null;
}
private void registerAll(final Path start, final WatchService watchService) throws Exception {
// Register the directory and its subdirectories recursively
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
fileWatcherMonitor = null;
}
});
}
private void goThroughVersionMigrationProtocol(String projectPath, ProjectVO projectVo) {
@@ -10,6 +10,7 @@ import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.Pools;
import com.kotcrab.vis.ui.widget.VisLabel;
import games.rednblack.editor.utils.Guide;
@@ -73,7 +74,7 @@ public class RulersUI extends Actor {
private final Circle tmpCircle = new Circle();
private final Color tmpColor = new Color();
private final HashMap<Integer, String> labelTextCache = new HashMap<>();
private final IntMap<String> labelTextCache = new IntMap<>();
public RulersUI() {
horizontalRect = new Rectangle();
@@ -324,7 +325,8 @@ public class RulersUI extends Actor {
VisLabel label = Pools.obtain(VisLabel.class);
label.setColor(TEXT_COLOR);
int textNumber = (int) Math.abs(worldStartPointCpy.y + iterator * gridMeasuringSize);
labelTextCache.putIfAbsent(textNumber, "");
if (labelTextCache.get(textNumber) == null)
labelTextCache.put(textNumber, "");
String lblText = labelTextCache.get(textNumber);
if (lblText.equals("")) {
lblText = verticalize(textNumber + "");
@@ -26,7 +26,7 @@ public class CodeEditorDialogMediator extends Mediator<CodeEditorDialog> {
@Override
public void listNotificationInterests(Interests interests) {
interests.add(MsgAPI.OPEN_CODE_EDITOR,
MsgAPI.PROJECT_FILE_MODIFIED);
MsgAPI.PROJECT_FILE_MODIFIED, MsgAPI.PROJECT_FILE_CREATED);
}
@Override
@@ -47,6 +47,7 @@ public class CodeEditorDialogMediator extends Mediator<CodeEditorDialog> {
readObservedFile();
}
break;
case MsgAPI.PROJECT_FILE_CREATED:
case MsgAPI.PROJECT_FILE_MODIFIED:
if (!viewComponent.hasParent())
break;