diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java index b90da2d32..765e9a750 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java @@ -52,6 +52,13 @@ public class BackgroundWorker extends SwingWorker { System.gc(); LOG.debug("Memory usage: Before decompile: {}", UiUtils.memoryInfo()); runJob(cache.getDecompileJob()); + LOG.debug("Memory usage: After decompile: {}", UiUtils.memoryInfo()); + + LOG.info("Memory usage: Before refresh: {}", UiUtils.memoryInfo()); + long start = System.nanoTime(); + runJob(cache.getRefreshJob()); + cache.setRefreshJob(null); + LOG.info("Memory usage: After refresh: {}, refresh took {} ms", UiUtils.memoryInfo(), (System.nanoTime() - start) / 1000000); LOG.debug("Memory usage: Before index: {}", UiUtils.memoryInfo()); runJob(cache.getIndexJob()); @@ -74,7 +81,7 @@ public class BackgroundWorker extends SwingWorker { } private void runJob(BackgroundJob job) { - if (isCancelled()) { + if (isCancelled() || job == null) { return; } progressPane.changeLabel(this, job.getInfoString()); diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/RefreshJob.java b/jadx-gui/src/main/java/jadx/gui/jobs/RefreshJob.java new file mode 100644 index 000000000..830feab45 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/jobs/RefreshJob.java @@ -0,0 +1,28 @@ +package jadx.gui.jobs; + +import java.util.Set; + +import jadx.api.JavaClass; +import jadx.gui.JadxWrapper; + +public class RefreshJob extends BackgroundJob { + + private Set refreshClasses; + + public RefreshJob(JadxWrapper jadxWrapper, int threadsCount, Set refreshClasses) { + super(jadxWrapper, threadsCount); + this.refreshClasses = refreshClasses; + } + + protected void runJob() { + for (final JavaClass cls : refreshClasses) { + addTask(cls::refresh); + } + } + + @Override + public String getInfoString() { + return "Refreshing: "; + } + +} 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 f28b6d7b6..6ed0bba4d 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -396,12 +396,12 @@ public class MainWindow extends JFrame { cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount)); } - void resetIndex() { + public void resetIndex() { int threadsCount = settings.getThreadsCount(); cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount)); } - private synchronized void runBackgroundJobs() { + synchronized void runBackgroundJobs() { cancelBackgroundJobs(); backgroundWorker = new BackgroundWorker(cacheObject, progressPane); if (settings.isAutoStartJobs()) { @@ -414,6 +414,12 @@ public class MainWindow extends JFrame { } } + synchronized void runBackgroundRefreshAndIndexJobs() { + cancelBackgroundJobs(); + backgroundWorker = new BackgroundWorker(cacheObject, progressPane); + backgroundWorker.exec(); + } + public synchronized void cancelBackgroundJobs() { backgroundExecutor.cancelAll(); if (backgroundWorker != null) { @@ -517,7 +523,7 @@ public class MainWindow extends JFrame { treeModel.reload(); } - void reloadTree() { + public void reloadTree() { treeReloading = true; treeModel.reload(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java index b36ee7767..9d84be936 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java @@ -12,6 +12,7 @@ import java.nio.file.StandardCopyOption; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.swing.*; @@ -27,6 +28,8 @@ import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.RootNode; import jadx.core.dex.visitors.RenameVisitor; import jadx.core.utils.files.InputFile; +import jadx.gui.jobs.IndexJob; +import jadx.gui.jobs.RefreshJob; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JField; import jadx.gui.treemodel.JMethod; @@ -35,6 +38,7 @@ import jadx.gui.treemodel.JPackage; import jadx.gui.ui.codearea.ClassCodeContentPanel; import jadx.gui.ui.codearea.CodeArea; import jadx.gui.ui.codearea.CodePanel; +import jadx.gui.utils.CacheObject; import jadx.gui.utils.CodeUsageInfo; import jadx.gui.utils.NLS; import jadx.gui.utils.TextStandardActions; @@ -152,6 +156,7 @@ public class RenameDialog extends JDialog { } private void rename() { + long start = System.nanoTime(); String renameText = renameField.getText(); if (renameText == null || renameText.length() == 0 || codeArea.getText() == null) { return; @@ -166,7 +171,13 @@ public class RenameDialog extends JDialog { dispose(); return; } - refreshState(root); + long refreshStart = System.nanoTime(); + int classes = refreshState(root); + long refreshTime = (System.nanoTime() - refreshStart) / 1000000; + long totalTime = (System.nanoTime() - start) / 1000000; + LOG.info("refreshState() took {} ms to update state, {} classes will be refreshed in background ({} ms total)", refreshTime, + classes, + totalTime); dispose(); } @@ -198,42 +209,73 @@ public class RenameDialog extends JDialog { return true; } - private void refreshState(RootNode rootNode) { + private int refreshState(RootNode rootNode) { RenameVisitor renameVisitor = new RenameVisitor(); renameVisitor.init(rootNode); mainWindow.getCacheObject().getNodeCache().refresh(node); - TabbedPane tabbedPane = mainWindow.getTabbedPane(); - refreshTabs(tabbedPane); - refreshUsages(); - mainWindow.resetIndex(); + Set updatedClasses = getUpdatedClasses(); + mainWindow.reloadTree(); + refreshTabs(mainWindow.getTabbedPane(), updatedClasses); + + if (updatedClasses.size() > 0) { + setRefreshTask(updatedClasses); + } + + return updatedClasses.size(); } - private void refreshTabs(TabbedPane tabbedPane) { + private void refreshTabs(TabbedPane tabbedPane, Set updatedClasses) { for (Map.Entry panel : tabbedPane.getOpenTabs().entrySet()) { ContentPanel contentPanel = panel.getValue(); if (contentPanel instanceof ClassCodeContentPanel) { JNode node = panel.getKey(); - node.getRootClass().refresh(); // Update code cache - ClassCodeContentPanel codePanel = (ClassCodeContentPanel) contentPanel; - CodePanel javaPanel = codePanel.getJavaCodePanel(); - javaPanel.refresh(); - tabbedPane.refresh(node); + JClass rootClass = node.getRootClass(); + JavaClass javaClass = rootClass.getCls(); + if (updatedClasses.contains(javaClass) || node.getRootClass().getCls() == javaClass) { + LOG.info("Refreshing rootClass " + javaClass.getRawName()); + rootClass.refresh(); // Update code cache + ClassCodeContentPanel codePanel = (ClassCodeContentPanel) contentPanel; + CodePanel javaPanel = codePanel.getJavaCodePanel(); + javaPanel.refresh(); + tabbedPane.refresh(node); + updatedClasses.remove(javaClass); + } } } } - private void refreshUsages() { + private Set getUpdatedClasses() { + Set usageClasses = new HashSet<>(); CodeUsageInfo usageInfo = mainWindow.getCacheObject().getUsageInfo(); - if (usageInfo == null) { - return; + if (usageInfo != null) { + usageInfo.getUsageList(node).forEach((node) -> { + JavaClass rootClass = node.getRootClass().getCls(); + // LOG.info("updateUsages(): Going to update class {}", rootClass.getRealFullName()); + usageClasses.add(rootClass); + }); + // usageClasses.parallelStream().forEach(JavaClass::refresh); } + return usageClasses; + } - HashSet usageClasses = new HashSet<>(); - usageInfo.getUsageList(node).forEach((node) -> usageClasses.add(node.getRootClass().getCls())); - usageClasses.forEach(JavaClass::refresh); + private void setRefreshTask(Set refreshClasses) { + CacheObject cache = mainWindow.getCacheObject(); + RefreshJob refreshJob = new RefreshJob(mainWindow.getWrapper(), mainWindow.getSettings().getThreadsCount(), refreshClasses); + LOG.info("Waiting for old refreshJob"); + while (cache.getRefreshJob() != null) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + return; + } + } + LOG.info("Old refreshJob finished"); + cache.setRefreshJob(refreshJob); + cache.setIndexJob(new IndexJob(mainWindow.getWrapper(), mainWindow.getCacheObject(), mainWindow.getSettings().getThreadsCount())); + mainWindow.runBackgroundRefreshAndIndexJobs(); } private void initCommon() { diff --git a/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java b/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java index fc47dd81c..4e523568c 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/CacheObject.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable; import jadx.gui.jobs.DecompileJob; import jadx.gui.jobs.IndexJob; +import jadx.gui.jobs.RefreshJob; import jadx.gui.ui.SearchDialog; import jadx.gui.utils.search.TextSearchIndex; @@ -14,6 +15,7 @@ public class CacheObject { private DecompileJob decompileJob; private IndexJob indexJob; + private RefreshJob refreshJob; private TextSearchIndex textIndex; private CodeUsageInfo usageInfo; @@ -89,4 +91,12 @@ public class CacheObject { public Set getLastSearchOptions() { return lastSearchOptions; } + + public RefreshJob getRefreshJob() { + return refreshJob; + } + + public void setRefreshJob(RefreshJob refreshJob) { + this.refreshJob = refreshJob; + } }