From cc1beab0b50d85f5d5a39bf9163e204d3052239b Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:15:23 +0000 Subject: [PATCH] fix(gui): use correct threads for code from close and reload actions (#2684) --- .../jadx/gui/tree/TreeExpansionService.java | 11 ++- .../src/main/java/jadx/gui/ui/MainWindow.java | 94 ++++++++++--------- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/jadx-gui/src/main/java/jadx/gui/tree/TreeExpansionService.java b/jadx-gui/src/main/java/jadx/gui/tree/TreeExpansionService.java index 81b9c2170..e18d5a724 100644 --- a/jadx-gui/src/main/java/jadx/gui/tree/TreeExpansionService.java +++ b/jadx-gui/src/main/java/jadx/gui/tree/TreeExpansionService.java @@ -21,13 +21,13 @@ import jadx.api.metadata.ICodeNodeRef; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.gui.jobs.LoadTask; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JPackage; import jadx.gui.treemodel.JRoot; import jadx.gui.ui.MainWindow; import jadx.gui.utils.JNodeCache; -import jadx.gui.utils.NLS; import jadx.gui.utils.UiUtils; public class TreeExpansionService { @@ -62,9 +62,9 @@ public class TreeExpansionService { } public void load(List treeExpansions) { - List expandedPaths = new ArrayList<>(); - mainWindow.getBackgroundExecutor().execute(NLS.str("progress.load"), + mainWindow.getBackgroundExecutor().execute(new LoadTask<>( () -> { + List expandedPaths = new ArrayList<>(); loadPaths(treeExpansions, expandedPaths); // send expand event to load sub-nodes and wait for completion UiUtils.uiRunAndWait(() -> expandedPaths.forEach(path -> { @@ -74,11 +74,12 @@ public class TreeExpansionService { throw new JadxRuntimeException("Tree expand error", e); } })); + return expandedPaths; }, - s -> { + expandedPaths -> { // expand paths after a loading task is finished expandedPaths.forEach(tree::expandPath); - }); + })); } private void loadPaths(List treeExpansions, List expandedPaths) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index b797508f7..e32f0f379 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -32,7 +32,6 @@ import java.util.List; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import javax.swing.AbstractAction; @@ -377,8 +376,10 @@ public class MainWindow extends JFrame { if (!ensureProjectIsSaved()) { return; } - closeAll(); - updateProject(new JadxProject(this)); + UiUtils.bgRun(() -> { + closeAll(); + updateProject(new JadxProject(this)); + }); } private void saveProject() { @@ -540,6 +541,7 @@ public class MainWindow extends JFrame { synchronized (ReloadProject.EVENT) { saveAll(); closeAll(); + System.gc(); loadFiles(() -> { menuBar.reloadShortcuts(); events().send(ReloadSettingsWindow.INSTANCE); @@ -571,25 +573,16 @@ public class MainWindow extends JFrame { onFinish.run(); return; } - AtomicReference wrapperException = new AtomicReference<>(); backgroundExecutor.execute(NLS.str("progress.load"), () -> { try { wrapper.open(); } catch (Exception e) { - wrapperException.set(e); + LOG.error("Project load error", e); + closeAll(); } }, status -> { - if (wrapperException.get() != null) { - closeAll(); - Exception e = wrapperException.get(); - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new JadxRuntimeException("Project load error", e); - } - } if (status == TaskStatus.CANCEL_BY_MEMORY) { showHeapUsageBar(); UiUtils.errorMessage(this, NLS.str("message.memoryLow")); @@ -600,8 +593,7 @@ public class MainWindow extends JFrame { return; } checkLoadedStatus(); - onOpen(); - onFinish.run(); + onOpen(onFinish); }); } @@ -612,19 +604,21 @@ public class MainWindow extends JFrame { } private void closeAll() { - notifyLoadListeners(false); + UiUtils.notUiThreadGuard(); cancelBackgroundJobs(); - navController.reset(); - tabbedPane.reset(); - clearTree(); - resetCache(); - LogCollector.getInstance().reset(); + UiUtils.uiRunAndWait(() -> { + tabsController.forceCloseAllTabs(); + tabbedPane.reset(); + navController.reset(); + shortcutsController.reset(); + clearTree(); + UiUtils.resetClipboardOwner(); + update(); + }); wrapper.close(); - tabsController.forceCloseAllTabs(); - shortcutsController.reset(); - UiUtils.resetClipboardOwner(); - System.gc(); - UiUtils.uiRun(this::update); + LogCollector.getInstance().reset(); + resetCache(); + notifyLoadListeners(false); } private void checkLoadedStatus() { @@ -647,21 +641,23 @@ public class MainWindow extends JFrame { } } - private void onOpen() { + private void onOpen(Runnable onFinish) { initTree(); updateLiveReload(project.isEnableLiveReload()); BreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent()); - treeExpansionService.load(project.getTreeExpansions()); List openTabs = project.getOpenTabs(this); - backgroundExecutor.execute(NLS.str("progress.load"), + backgroundExecutor.startLoading( () -> preLoadOpenTabs(openTabs), - status -> { + () -> { restoreOpenTabs(openTabs); - runInitialBackgroundJobs(); - notifyLoadListeners(true); update(); + notifyLoadListeners(true); + onFinish.run(); checkIfCodeHasNonPrintableChars(); + runInitialBackgroundJobs(); }); + // queue tree state restore after loading task + treeExpansionService.load(project.getTreeExpansions()); } public void passesReloaded() { @@ -1592,18 +1588,26 @@ public class MainWindow extends JFrame { if (!ensureProjectIsSaved()) { return; } - settings.setTreeWidth(treeSplitPane.getDividerLocation()); - settings.saveWindowPos(this); - settings.setMainWindowExtendedState(getExtendedState()); - if (debuggerPanel != null) { - saveSplittersInfo(); - } - heapUsageBar.reset(); - closeAll(); - - editorThemeManager.unload(); - dispose(); - System.exit(0); + UiUtils.bgRun(() -> { + try { + settings.setTreeWidth(treeSplitPane.getDividerLocation()); + settings.saveWindowPos(this); + settings.setMainWindowExtendedState(getExtendedState()); + if (debuggerPanel != null) { + saveSplittersInfo(); + } + closeAll(); + UiUtils.uiRunAndWait(() -> { + heapUsageBar.reset(); + editorThemeManager.unload(); + dispose(); + }); + } catch (Exception e) { + LOG.error("Close window error", e); + } finally { + System.exit(0); + } + }); } private void saveOpenTabs() {