Partial Fix For Slow jFileChooser

Start of fixing #272

This is a Swing / JDK bug - Using a different JDK is the recommended fix, but this work around helps cut the time waited by a lot.
This commit is contained in:
Konloch 2024-09-29 22:03:26 -06:00
parent d78ab844e1
commit 133526c0c6

AI 샘플 코드 생성 중입니다

Loading...
12 changed files with 291 additions and 220 deletions

View File

@ -53,6 +53,8 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static javax.swing.JOptionPane.QUESTION_MESSAGE;
import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -263,7 +265,8 @@ public class BytecodeViewer
TASK_MANAGER.start();
//setup the viewer
viewer.calledAfterLoad();
if(!cli)
viewer.calledAfterLoad();
//setup the recent files
Settings.resetRecentFilesMenu();

View File

@ -103,26 +103,33 @@ public class GlobalHotKeys
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(), "Select Zip Export", "Zip Archives", "zip");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
{
Configuration.setLastSaveDirectory(fc.getSelectedFile());
JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(), "Select Zip Export", "Zip Archives", "zip");
File file = MiscUtils.autoAppendFileExtension(".zip", fc.getSelectedFile());
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (!DialogUtils.canOverwriteFile(file))
return;
BytecodeViewer.updateBusyStatus(true);
Thread jarExport = new Thread(() ->
if (returnVal == JFileChooser.APPROVE_OPTION)
{
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), file.getAbsolutePath());
BytecodeViewer.updateBusyStatus(false);
}, "Jar Export");
jarExport.start();
Configuration.setLastSaveDirectory(fc.getSelectedFile());
File file = MiscUtils.autoAppendFileExtension(".zip", fc.getSelectedFile());
if (!DialogUtils.canOverwriteFile(file))
return;
BytecodeViewer.updateBusyStatus(true);
Thread jarExport = new Thread(() ->
{
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), file.getAbsolutePath());
BytecodeViewer.updateBusyStatus(false);
}, "Jar Export");
jarExport.start();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}, "Resource Export");

View File

@ -32,6 +32,7 @@ import java.awt.*;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import static the.bytecode.club.bytecodeviewer.Constants.VERSION;
import static the.bytecode.club.bytecodeviewer.Constants.NL;
@ -145,9 +146,9 @@ public class UpdateCheck implements Runnable
}
}
public static File promptFileSave(String description, String extension) throws IOException
public static File promptFileSave(String description, String extension) throws IOException, ExecutionException, InterruptedException
{
JFileChooser fc = new FileChooser(new File("./").getCanonicalFile(), "Select Save File", description, extension);
JFileChooser fc = FileChooser.create(new File("./").getCanonicalFile(), "Select Save File", description, extension);
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
File file = null;

View File

@ -61,6 +61,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static the.bytecode.club.bytecodeviewer.Configuration.useNewSettingsDialog;
import static the.bytecode.club.bytecodeviewer.Constants.VERSION;
@ -835,6 +837,10 @@ public class MainViewerGUI extends JFrame
public void calledAfterLoad()
{
deleteForeignOutdatedLibs.setSelected(Configuration.deleteForeignLibraries);
//preload the jFileChooser to fix https://stackoverflow.com/a/59165208
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(FileChooser.SINGLETON);
}
public int getFontSize()

View File

@ -26,40 +26,48 @@ import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author Konloch
* @since 6/25/2021
*/
public class FileChooser extends JFileChooser
public class FileChooser
{
public static final FutureTask<JFileChooser> SINGLETON = new FutureTask<>(JFileChooser::new);
public static final String EVERYTHING = "everything";
public FileChooser(File file, String title, String description, String... extensions)
public static JFileChooser create(File file, String title, String description, String... extensions) throws ExecutionException, InterruptedException
{
this(false, file, title, description, extensions);
return create(false, file, title, description, extensions);
}
public FileChooser(boolean skipFileFilter, File file, String title, String description, String... extensions)
public static JFileChooser create(boolean skipFileFilter, File file, String title, String description, String... extensions) throws ExecutionException, InterruptedException
{
JFileChooser chooser = SINGLETON.get();
Set<String> extensionSet = new HashSet<>(Arrays.asList(extensions));
setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
try
{
setSelectedFile(file);
chooser.setSelectedFile(file);
}
catch (Exception ignored)
{
}
setDialogTitle(title);
setFileHidingEnabled(false);
setAcceptAllFileFilterUsed(false);
chooser.setDialogTitle(title);
chooser.setFileHidingEnabled(false);
chooser.setAcceptAllFileFilterUsed(false);
chooser.resetChoosableFileFilters();
if (!skipFileFilter)
{
addChoosableFileFilter(new FileFilter()
chooser.addChoosableFileFilter(new FileFilter()
{
@Override
public boolean accept(File f)
@ -80,5 +88,7 @@ public class FileChooser extends JFileChooser
}
});
}
return chooser;
}
}

View File

@ -235,30 +235,37 @@ public class PluginWriter extends JFrame
if (savePath == null)
{
final String ext = FileNameUtils.getExtension(pluginName);
JFileChooser fc = new FileChooser(Configuration.getLastPluginDirectory(), "Save Plugin", "BCV Plugin", ext);
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
{
Configuration.setLastPluginDirectory(fc.getSelectedFile());
final String ext = FileNameUtils.getExtension(pluginName);
JFileChooser fc = FileChooser.create(Configuration.getLastPluginDirectory(), "Save Plugin", "BCV Plugin", ext);
File file = fc.getSelectedFile();
String path = file.getAbsolutePath();
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
Configuration.setLastPluginDirectory(fc.getSelectedFile());
//auto append extension
if (!path.endsWith("." + ext))
path += "." + ext;
File file = fc.getSelectedFile();
String path = file.getAbsolutePath();
if (!DialogUtils.canOverwriteFile(path))
//auto append extension
if (!path.endsWith("." + ext))
path += "." + ext;
if (!DialogUtils.canOverwriteFile(path))
return;
//swap from save-as to having a defined path each save
setSourceFile(new File(path));
}
else
{
return;
//swap from save-as to having a defined path each save
setSourceFile(new File(path));
}
}
else
catch (Exception e)
{
return;
e.printStackTrace();
}
}

View File

@ -63,42 +63,42 @@ public class ResourceDecompiling
MiscUtils.createNewThread("Decompile Save-All Thread", () ->
{
//signal to the user that BCV is performing an action in the background
BytecodeViewer.updateBusyStatus(true);
//auto compile before decompilation
if (!BytecodeViewer.autoCompileSuccessful())
return;
final JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(),
"Select Zip Export", "Zip Archives", "zip");
//if the user doesn't select a file then we should stop while we're ahead
if (fc.showSaveDialog(BytecodeViewer.viewer) != JFileChooser.APPROVE_OPTION)
return;
//set the last touched save directory for BCV
Configuration.setLastSaveDirectory(fc.getSelectedFile());
//get the save file and auto append zip extension
final File outputZip = MiscUtils.autoAppendFileExtension(".zip", fc.getSelectedFile());
//prompt the user for a dialogue override-this-file option if the file already exists
if (!DialogUtils.canOverwriteFile(outputZip))
return;
//this temporary jar file will be used to store the classes while BCV performs decompilation
File temporaryTargetJar = MiscUtils.deleteExistingFile(new File(TEMP_DIRECTORY + FS
+ "temp_" + MiscUtils.getRandomizedName() + ".jar"));
//extract all the loaded classes imported into BCV to the temporary target jar
JarUtils.saveAsJarClassesOnly(BytecodeViewer.getLoadedClasses(), temporaryTargetJar.getAbsolutePath());
//signal to the user that BCV is finished performing that action
BytecodeViewer.updateBusyStatus(false);
try
{
//signal to the user that BCV is performing an action in the background
BytecodeViewer.updateBusyStatus(true);
//auto compile before decompilation
if (!BytecodeViewer.autoCompileSuccessful())
return;
final JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(),
"Select Zip Export", "Zip Archives", "zip");
//if the user doesn't select a file then we should stop while we're ahead
if (fc.showSaveDialog(BytecodeViewer.viewer) != JFileChooser.APPROVE_OPTION)
return;
//set the last touched save directory for BCV
Configuration.setLastSaveDirectory(fc.getSelectedFile());
//get the save file and auto append zip extension
final File outputZip = MiscUtils.autoAppendFileExtension(".zip", fc.getSelectedFile());
//prompt the user for a dialogue override-this-file option if the file already exists
if (!DialogUtils.canOverwriteFile(outputZip))
return;
//this temporary jar file will be used to store the classes while BCV performs decompilation
File temporaryTargetJar = MiscUtils.deleteExistingFile(new File(TEMP_DIRECTORY + FS
+ "temp_" + MiscUtils.getRandomizedName() + ".jar"));
//extract all the loaded classes imported into BCV to the temporary target jar
JarUtils.saveAsJarClassesOnly(BytecodeViewer.getLoadedClasses(), temporaryTargetJar.getAbsolutePath());
//signal to the user that BCV is finished performing that action
BytecodeViewer.updateBusyStatus(false);
//handle the result of the user selection
switch (promptDecompilerUserSelect() + DECOMPILE_SAVE_ALL)
{
@ -159,34 +159,35 @@ public class ResourceDecompiling
MiscUtils.createNewThread("Decompile Save Opened Resource", () ->
{
//signal to the user that BCV is performing an action in the background
BytecodeViewer.updateBusyStatus(true);
//auto compile before decompilation
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(), "Select Java Files", "Java Source Files", "java");
//if the user doesn't select a file then we should stop while we're ahead
if (fc.showSaveDialog(BytecodeViewer.viewer) != JFileChooser.APPROVE_OPTION)
return;
//set the last touched save directory for BCV
Configuration.setLastSaveDirectory(fc.getSelectedFile());
//get the save file and auto append java extension
File file = MiscUtils.autoAppendFileExtension(".java", fc.getSelectedFile());
//prompt the user for a dialogue override-this-file option if the file already exists
if (!DialogUtils.canOverwriteFile(file))
return;
//signal to the user that BCV is finished performing that action
BytecodeViewer.updateBusyStatus(false);
try
{
//signal to the user that BCV is performing an action in the background
BytecodeViewer.updateBusyStatus(true);
//auto compile before decompilation
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(),
"Select Java Files", "Java Source Files", "java");
//if the user doesn't select a file then we should stop while we're ahead
if (fc.showSaveDialog(BytecodeViewer.viewer) != JFileChooser.APPROVE_OPTION)
return;
//set the last touched save directory for BCV
Configuration.setLastSaveDirectory(fc.getSelectedFile());
//get the save file and auto append java extension
File file = MiscUtils.autoAppendFileExtension(".java", fc.getSelectedFile());
//prompt the user for a dialogue override-this-file option if the file already exists
if (!DialogUtils.canOverwriteFile(file))
return;
//signal to the user that BCV is finished performing that action
BytecodeViewer.updateBusyStatus(false);
//handle the result of the user selection
switch (promptDecompilerUserSelect() + DECOMPILE_OPENED_ONLY_ALL)
{

View File

@ -90,37 +90,44 @@ public class APKExport implements Exporter
Thread exportThread = new Thread(() ->
{
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(), "Select APK Export", "Android APK", "apk");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
{
Configuration.setLastSaveDirectory(fc.getSelectedFile());
final File file = MiscUtils.autoAppendFileExtension(".apk", fc.getSelectedFile());
if (!DialogUtils.canOverwriteFile(file))
if (!BytecodeViewer.autoCompileSuccessful())
return;
Thread saveThread = new Thread(() ->
JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(), "Select APK Export", "Android APK", "apk");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
BytecodeViewer.updateBusyStatus(true);
final String input = TEMP_DIRECTORY + FS + MiscUtils.getRandomizedName() + ".jar";
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
Configuration.setLastSaveDirectory(fc.getSelectedFile());
Thread buildAPKThread = new Thread(() ->
final File file = MiscUtils.autoAppendFileExtension(".apk", fc.getSelectedFile());
if (!DialogUtils.canOverwriteFile(file))
return;
Thread saveThread = new Thread(() ->
{
APKTool.buildAPK(new File(input), file, finalContainer);
BytecodeViewer.updateBusyStatus(false);
}, "Process APK");
BytecodeViewer.updateBusyStatus(true);
final String input = TEMP_DIRECTORY + FS + MiscUtils.getRandomizedName() + ".jar";
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
buildAPKThread.start();
}, "Jar Export");
Thread buildAPKThread = new Thread(() ->
{
APKTool.buildAPK(new File(input), file, finalContainer);
BytecodeViewer.updateBusyStatus(false);
}, "Process APK");
saveThread.start();
buildAPKThread.start();
}, "Jar Export");
saveThread.start();
}
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
}, "Resource Export");

View File

@ -48,44 +48,51 @@ public class DexExport implements Exporter
Thread exportThread = new Thread(() ->
{
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(), "Select DEX Export", "Android DEX Files", "dex");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
{
Configuration.setLastSaveDirectory(fc.getSelectedFile());
final File file = fc.getSelectedFile();
String output = file.getAbsolutePath();
//auto append .dex
if (!output.endsWith(".dex"))
output += ".dex";
File outputPath = new File(output);
if (!DialogUtils.canOverwriteFile(outputPath))
if (!BytecodeViewer.autoCompileSuccessful())
return;
Thread saveAsJar = new Thread(() ->
JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(), "Select DEX Export", "Android DEX Files", "dex");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
BytecodeViewer.updateBusyStatus(true);
final String input = TEMP_DIRECTORY + FS + MiscUtils.getRandomizedName() + ".jar";
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
Configuration.setLastSaveDirectory(fc.getSelectedFile());
Thread saveAsDex = new Thread(() ->
final File file = fc.getSelectedFile();
String output = file.getAbsolutePath();
//auto append .dex
if (!output.endsWith(".dex"))
output += ".dex";
File outputPath = new File(output);
if (!DialogUtils.canOverwriteFile(outputPath))
return;
Thread saveAsJar = new Thread(() ->
{
Dex2Jar.saveAsDex(new File(input), outputPath);
BytecodeViewer.updateBusyStatus(true);
final String input = TEMP_DIRECTORY + FS + MiscUtils.getRandomizedName() + ".jar";
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
BytecodeViewer.updateBusyStatus(false);
}, "Process DEX");
Thread saveAsDex = new Thread(() ->
{
Dex2Jar.saveAsDex(new File(input), outputPath);
saveAsDex.start();
}, "Jar Export");
BytecodeViewer.updateBusyStatus(false);
}, "Process DEX");
saveAsJar.start();
saveAsDex.start();
}, "Jar Export");
saveAsJar.start();
}
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
}, "Resource Export");

View File

@ -42,29 +42,37 @@ public class RunnableJarExporter implements Exporter
Thread exportThread = new Thread(() ->
{
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(), "Select Jar Export", "Jar Archives", "jar");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
{
Configuration.setLastSaveDirectory(fc.getSelectedFile());
File file = fc.getSelectedFile();
String path = file.getAbsolutePath();
//auto append .jar
if (!path.endsWith(".jar"))
path += ".jar";
if (!DialogUtils.canOverwriteFile(path))
if (!BytecodeViewer.autoCompileSuccessful())
return;
new ExportJar(path).setVisible(true);
JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(), "Select Jar Export", "Jar Archives", "jar");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
Configuration.setLastSaveDirectory(fc.getSelectedFile());
File file = fc.getSelectedFile();
String path = file.getAbsolutePath();
//auto append .jar
if (!path.endsWith(".jar"))
path += ".jar";
if (!DialogUtils.canOverwriteFile(path))
return;
new ExportJar(path).setVisible(true);
}
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
}, "Runnable Jar Export");
exportThread.start();
}
}

View File

@ -46,27 +46,38 @@ public class ZipExport implements Exporter
if (!BytecodeViewer.autoCompileSuccessful())
return;
JFileChooser fc = new FileChooser(Configuration.getLastSaveDirectory(), "Select Zip Export", "Zip Archives", "zip");
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
{
Configuration.setLastSaveDirectory(fc.getSelectedFile());
JFileChooser fc = FileChooser.create(Configuration.getLastSaveDirectory(),
"Select Zip Export", "Zip Archives", "zip");
final File file = MiscUtils.autoAppendFileExtension(".zip", fc.getSelectedFile()); //auto append .zip extension
if (!DialogUtils.canOverwriteFile(file))
return;
BytecodeViewer.updateBusyStatus(true);
Thread saveThread = new Thread(() ->
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), file.getAbsolutePath());
BytecodeViewer.updateBusyStatus(false);
}, "Jar Export");
saveThread.start();
Configuration.setLastSaveDirectory(fc.getSelectedFile());
final File file = MiscUtils.autoAppendFileExtension(".zip", fc.getSelectedFile()); //auto append .zip extension
if (!DialogUtils.canOverwriteFile(file))
return;
BytecodeViewer.updateBusyStatus(true);
Thread saveThread = new Thread(() ->
{
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), file.getAbsolutePath());
BytecodeViewer.updateBusyStatus(false);
}, "Jar Export");
saveThread.start();
}
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
}, "Resource Export");
exportThread.start();
}
}

View File

@ -92,46 +92,49 @@ public class DialogUtils
*/
public static File fileChooser(String title, String description, File directory, FileFilter filter, OnOpenEvent onOpen, String... extensions)
{
Set<String> extensionSet = new HashSet<>(Arrays.asList(extensions));
try
{
Set<String> extensionSet = new HashSet<>(Arrays.asList(extensions));
final JFileChooser fc = new FileChooser(true, directory, title, description, extensions);
final JFileChooser fc = FileChooser.create(true, directory, title, description, extensions);
if (filter != null)
fc.addChoosableFileFilter(filter);
else
fc.addChoosableFileFilter(new FileFilter()
{
@Override
public boolean accept(File f)
if (filter != null)
fc.addChoosableFileFilter(filter);
else
fc.addChoosableFileFilter(new FileFilter()
{
if (f.isDirectory())
return true;
@Override
public boolean accept(File f)
{
if (f.isDirectory())
return true;
if (extensions[0].equals(EVERYTHING))
return true;
if (extensions[0].equals(EVERYTHING))
return true;
return extensionSet.contains(MiscUtils.extension(f.getAbsolutePath()));
}
return extensionSet.contains(MiscUtils.extension(f.getAbsolutePath()));
}
@Override
public String getDescription()
{
return description;
}
});
@Override
public String getDescription()
{
return description;
}
});
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
File file = fc.getSelectedFile();
onOpen.onOpen(file);
return file;
}
catch (Exception e1)
{
BytecodeViewer.handleException(e1);
}
}
catch (Exception e)
{
BytecodeViewer.handleException(e);
}
return null;
}