gui: Perform refresh of non-displayed classes in background thread
After renaming some classes needs to be redecompiled to reflect new state. Move recompilation of non-displayed classes to background thread. This should improve performance on weak machines.
This commit is contained in:
@@ -52,6 +52,13 @@ public class BackgroundWorker extends SwingWorker<Void, Void> {
|
||||
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<Void, Void> {
|
||||
}
|
||||
|
||||
private void runJob(BackgroundJob job) {
|
||||
if (isCancelled()) {
|
||||
if (isCancelled() || job == null) {
|
||||
return;
|
||||
}
|
||||
progressPane.changeLabel(this, job.getInfoString());
|
||||
|
||||
@@ -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<JavaClass> refreshClasses;
|
||||
|
||||
public RefreshJob(JadxWrapper jadxWrapper, int threadsCount, Set<JavaClass> refreshClasses) {
|
||||
super(jadxWrapper, threadsCount);
|
||||
this.refreshClasses = refreshClasses;
|
||||
}
|
||||
|
||||
protected void runJob() {
|
||||
for (final JavaClass cls : refreshClasses) {
|
||||
addTask(cls::refresh);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInfoString() {
|
||||
return "Refreshing: ";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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<JavaClass> 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<JavaClass> updatedClasses) {
|
||||
for (Map.Entry<JNode, ContentPanel> 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<JavaClass> getUpdatedClasses() {
|
||||
Set<JavaClass> 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<JavaClass> usageClasses = new HashSet<>();
|
||||
usageInfo.getUsageList(node).forEach((node) -> usageClasses.add(node.getRootClass().getCls()));
|
||||
usageClasses.forEach(JavaClass::refresh);
|
||||
private void setRefreshTask(Set<JavaClass> 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() {
|
||||
|
||||
@@ -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<SearchDialog.SearchOptions> getLastSearchOptions() {
|
||||
return lastSearchOptions;
|
||||
}
|
||||
|
||||
public RefreshJob getRefreshJob() {
|
||||
return refreshJob;
|
||||
}
|
||||
|
||||
public void setRefreshJob(RefreshJob refreshJob) {
|
||||
this.refreshJob = refreshJob;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user