Как я могу перезапустить приложение Java AWT? У меня есть кнопка, к которой я прикрепил обработчик событий. Какой код использовать для перезапуска приложения?
Я хочу делать то же самое, что Application.Restart()
и в приложении C #.
Как я могу перезапустить приложение Java AWT? У меня есть кнопка, к которой я прикрепил обработчик событий. Какой код использовать для перезапуска приложения?
Я хочу делать то же самое, что Application.Restart()
и в приложении C #.
Ответы:
Конечно, можно перезапустить приложение Java.
Следующий метод показывает способ перезапуска приложения Java:
public void restartApplication()
{
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());
/* is it a jar file? */
if(!currentJar.getName().endsWith(".jar"))
return;
/* Build command: java -jar application.jar */
final ArrayList<String> command = new ArrayList<String>();
command.add(javaBin);
command.add("-jar");
command.add(currentJar.getPath());
final ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
System.exit(0);
}
В основном он делает следующее:
MyClassInTheJar
класс для поиска самого местоположения банки)System.exit(0)
завершает ли дочерний процесс, совпадает с тем, действительно ли этот ответ работает и почему. Если вы не можете дать разумное объяснение вместе со своим ответом, вы сделали плохую работу. Ответ, который дает больше вопросов, чем ответов, не является примером исчерпывающего ответа. Хорошие ответы не только показывают код, но и объясняют, как и почему они работают, какие недостатки есть и каковы альтернативы. Вы даже не пытались прикрыть эти вещи.
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
StringBuilder cmd = new StringBuilder();
cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
cmd.append(jvmArg + " ");
}
cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
cmd.append(Main.class.getName()).append(" ");
for (String arg : args) {
cmd.append(arg).append(" ");
}
Runtime.getRuntime().exec(cmd.toString());
System.exit(0);
}
}
Посвящается всем, кто говорит, что это невозможно.
Эта программа собирает всю доступную информацию для восстановления исходной командной строки. Затем он запускает ее, и, поскольку это та же самая команда, ваше приложение запускается во второй раз. Затем мы выходим из исходной программы, дочерняя программа продолжает работать (даже в Linux) и делает то же самое.
ПРЕДУПРЕЖДЕНИЕ : если вы запустите это, имейте в виду, что он никогда не завершит создание новых процессов, как форк-бомба .
ManagementFactory.getRuntimeMXBean().getInputArguments()
предоставит вам только входные аргументы, переданные JVM. Он пропускает параметры, переданные вашему приложению. например, java -jar start.jar -MISSED_PARAM=true
. На Oracle JVM вы можете получить эти параметры, используя System.getProperty("sun.java.command")
.
ProcessBuilder
и inheritIO()
, дочерняя виртуальная машина может быть запущена таким же образом, как и родительская виртуальная машина.
В принципе, нельзя. По крайней мере, не надежным способом. Однако в этом нет необходимости.
Чтобы перезапустить программу Java, вам необходимо перезапустить JVM. Чтобы перезапустить JVM, вам необходимо
Найдите используемую java
пусковую установку. Вы можете попробовать, System.getProperty("java.home")
но нет гарантии, что он действительно будет указывать на средство запуска, которое использовалось для запуска вашего приложения. (Возвращаемое значение может не указывать на JRE, используемую для запуска приложения, или оно могло быть переопределено -Djava.home
.)
Вы бы предположительно хотите выполнить первоначальную память настроек и т.д. ( -Xmx
, -Xms
, ...) , так что вы должны выяснить , какие параметры которой используется для запуска первой виртуальной машины Java. Вы можете попробовать использовать, ManagementFactory.getRuntimeMXBean().getInputArguments()
но нет гарантии, что это будет отражать используемые настройки. Это даже прописано в документации этого метода:
Как правило, не все параметры командной строки для команды java передаются виртуальной машине Java. Таким образом, возвращаемые входные аргументы могут не включать все параметры командной строки.
Если ваша программа считывает ввод с Standard.in
исходного stdin, он будет потерян при перезапуске.
Многие из этих уловок и уловок не работают при наличии файла SecurityManager
.
Я рекомендую вам спроектировать свое приложение так, чтобы было легко очистить все, а после этого создать новый экземпляр вашего «основного» класса.
Многие приложения предназначены только для создания экземпляра в основном методе:
public class MainClass {
...
public static void main(String[] args) {
new MainClass().launch();
}
...
}
Используя этот шаблон, должно быть достаточно легко сделать что-то вроде:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
и пусть launch()
вернет true тогда и только тогда, когда приложение было закрыто таким образом, что его нужно перезапустить.
Строго говоря, Java-программа не может перезапустить себя, поскольку для этого она должна убить JVM, в которой она запущена, а затем запустить ее снова, но как только JVM больше не работает (убита), никаких действий предпринимать нельзя.
Вы можете проделать некоторые трюки с пользовательскими загрузчиками классов для загрузки, упаковки и повторного запуска компонентов AWT, но это, вероятно, вызовет много головной боли в отношении цикла событий графического интерфейса пользователя.
В зависимости от того, как запускается приложение, вы можете запустить JVM в сценарии-оболочке, который содержит цикл do / while, который продолжается, пока JVM выходит с определенным кодом, тогда приложение AWT должно будет вызвать System.exit(RESTART_CODE)
. Например, в псевдокоде скрипта:
DO
# Launch the awt program
EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)
Приложение AWT должно выйти из JVM с чем-то другим, кроме RESTART_CODE, при "нормальном" завершении, которое не требует перезапуска.
JavaApplicationStub
... Не уверен, есть ли простой способ обойти это.
Eclipse обычно перезагружается после установки плагина. Они делают это с помощью оболочки eclipse.exe (приложение запуска) для Windows. Это приложение запускает базовую jar-среду eclipse runner, и если java-приложение eclipse завершается с кодом перезапуска, eclipse.exe перезапускает рабочую среду. Вы можете создать аналогичный фрагмент собственного кода, сценарий оболочки или другую оболочку кода Java для перезапуска.
Windows
public void restartApp(){
// This launches a new instance of application dirctly,
// remember to add some sleep to the start of the cmd file to make sure current instance is
// completely terminated, otherwise 2 instances of the application can overlap causing strange
// things:)
new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
System.exit(0);
}
/ мин для запуска скрипта в свернутом окне
^ & выйти, чтобы закрыть окно cmd после завершения
образец сценария cmd может быть
@echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar
спать 10 спать 10 секунд
Если вам действительно нужно перезапустить приложение, вы можете написать отдельное приложение для его запуска ...
На этой странице представлено множество различных примеров для разных сценариев:
Хотя этот вопрос старый и на него дан ответ, я наткнулся на проблему с некоторыми решениями и решил добавить свое предложение в смесь.
Проблема с некоторыми решениями заключается в том, что они создают единую командную строку. Это создает проблемы, когда некоторые параметры содержат пробелы, особенно java.home .
Например, в окнах строка
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Может вернуть что-то вроде этого:C:\Program Files\Java\jre7\bin\java
Эта строка должна быть заключена в кавычки или экранирована из-за пробела в Program Files
. Не большая проблема, но несколько раздражающая и подверженная ошибкам, особенно в кроссплатформенных приложениях.
Поэтому мое решение строит команду как массив команд:
public static void restart(String[] args) {
ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
// Java
commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
// Jvm arguments
for (String jvmArg : jvmArgs) {
commands.add(jvmArg);
}
// Classpath
commands.add("-cp");
commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());
// Class to be executed
commands.add(BGAgent.class.getName());
// Command line arguments
for (String arg : args) {
commands.add(arg);
}
File workingDir = null; // Null working dir means that the child uses the same working directory
String[] env = null; // Null env means that the child uses the same environment
String[] commandArray = new String[commands.size()];
commandArray = commands.toArray(commandArray);
try {
Runtime.getRuntime().exec(commandArray, env, workingDir);
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
Я сам исследовал эту тему, когда наткнулся на этот вопрос.
Несмотря на то, что ответ уже принят, я все же хотел бы предложить альтернативный подход для полноты картины. В частности, очень гибким решением послужил Apache Ant.
По сути, все сводится к файлу сценария Ant с одной задачей выполнения Java (см. Здесь и здесь ), вызываемой из кода Java (см. Здесь ). Этот код Java, который может быть запуском метода , может быть частью приложения, которое необходимо перезапустить. Приложение должно иметь зависимость от библиотеки Apache Ant (jar).
Всякий раз, когда приложение необходимо перезапустить, оно должно вызывать метод launch и выходить из виртуальной машины. Java-задача Ant должна иметь вилку параметров и порождение. установлено значение true.
Вот пример сценария Ant:
<project name="applaucher" default="launch" basedir=".">
<target name="launch">
<java classname="package.MasinClass" fork="true" spawn="true">
<jvmarg value="-splash:splash.jpg"/>
<jvmarg value="-D other VM params"/>
<classpath>
<pathelement location="lib-1.jar" />
...
<pathelement location="lib-n.jar" />
</classpath>
</java>
</target>
</project>
Код для метода запуска может выглядеть примерно так:
public final void launch(final String antScriptFile) {
/* configure Ant and execute the task */
final File buildFile = new File(antScriptFile);
final Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath());
final DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
p.addBuildListener(consoleLogger);
try {
p.fireBuildStarted();
p.init();
final ProjectHelper helper = ProjectHelper.getProjectHelper();
p.addReference("ant.projectHelper", helper);
helper.parse(p, buildFile);
p.executeTarget(p.getDefaultTarget());
p.fireBuildFinished(null);
} catch (final BuildException e) {
p.fireBuildFinished(e);
}
/* exit the current VM */
System.exit(0);
}
Очень удобно здесь то, что один и тот же скрипт используется как для первоначального запуска приложения, так и для перезапуска.
Просто добавляю информацию, которой нет в других ответах.
/proc/self/cmdline
доступенЕсли вы работаете в среде, которая предоставляет procfs и, следовательно, имеет /proc
доступную файловую систему (что означает, что это не переносимое решение), вы можете прочитать Java /proc/self/cmdline
, чтобы перезапустить себя, например:
public static void restart() throws IOException {
new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
return in.readLine();
}
}
В /proc/self/cmdline
доступных системах это, вероятно, самый элегантный способ «перезапустить» текущий процесс Java из Java. Никакого JNI не требуется, не требуется угадывание путей и прочего. Это также позаботится обо всех параметрах JVM, переданных в java
двоичный файл. Командная строка будет полностью идентична строке текущего процесса JVM.
Многие системы UNIX, включая GNU / Linux (включая Android), в настоящее время имеют procfs. Однако в некоторых системах, таких как FreeBSD, она устарела и постепенно прекращается. Mac OS X является исключением в том смысле, что в ней нет procfs . В Windows также нет procfs . Cygwin имеет procfs но он невидим для Java, потому что он виден только приложениям, использующим библиотеки DLL Cygwin вместо системных вызовов Windows, а Java не знает о Cygwin.
ProcessBuilder.inheritIO()
По умолчанию stdin
/ stdout
/ stderr
(в Java называется System.in
/ System.out
/ System.err
) запущенного процесса установлены каналы, которые позволяют текущему запущенному процессу взаимодействовать с вновь запущенным процессом. Если вы хотите перезапустить текущий процесс, скорее всего, это не то, что вам нужно . Вместо этого вы бы хотели, чтобы stdin
/ stdout
/ stderr
были такими же, как у текущей виртуальной машины. Это называется по наследству . Вы можете сделать это, вызвав inheritIO()
свой ProcessBuilder
экземпляр.
Частый случай использования restart()
функции - перезапуск приложения после обновления. В последний раз, когда я пробовал это в Windows, это было проблематично. При замене .jar
файла приложения новой версией приложение начало вести себя неправильно и выдавать исключения для .jar
файла. Я просто говорю, на случай, если это ваш вариант использования. Тогда я решил проблему, заключив приложение в пакетный файл и используя волшебное возвращаемое значение из того, System.exit()
что я запросил в пакетном файле, и вместо этого командный файл перезапустил приложение.
Старый вопрос и все такое. Но это еще один способ, который дает некоторые преимущества.
В Windows вы можете попросить планировщик задач снова запустить ваше приложение. Это дает преимущество в том, что вы ждете определенное время перед перезапуском приложения. Вы можете зайти в диспетчер задач и удалить задачу, и она перестанет повторяться.
SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");
Calendar aCal = Calendar.getInstance();
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);
Похож на « улучшенный » ответ Йоды , но с дальнейшими улучшениями (как функциональными, так и удобочитаемыми и тестируемыми). Теперь его можно запускать безопасно, и он будет перезапускаться столько раз, сколько указано в программе.
JAVA_TOOL_OPTIONS
вариантов.public static void main(String[] args) throws Exception {
if (args.length == 0)
return;
else
args = Arrays.copyOf(args, args.length - 1);
List<String> command = new ArrayList<>(32);
appendJavaExecutable(command);
appendVMArgs(command);
appendClassPath(command);
appendEntryPoint(command);
appendArgs(command, args);
System.out.println(command);
try {
new ProcessBuilder(command).inheritIO().start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private static void appendJavaExecutable(List<String> cmd) {
cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}
private static void appendVMArgs(Collection<String> cmd) {
Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
if (javaToolOptions != null) {
Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
vmArguments = new ArrayList<>(vmArguments);
vmArguments.removeAll(javaToolOptionsList);
}
cmd.addAll(vmArguments);
}
private static void appendClassPath(List<String> cmd) {
cmd.add("-cp");
cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}
private static void appendEntryPoint(List<String> cmd) {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
StackTraceElement stackTraceElement = stackTrace[stackTrace.length - 1];
String fullyQualifiedClass = stackTraceElement.getClassName();
String entryMethod = stackTraceElement.getMethodName();
if (!entryMethod.equals("main"))
throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);
cmd.add(fullyQualifiedClass);
}
private static void appendArgs(List<String> cmd, String[] args) {
cmd.addAll(Arrays.asList(args));
}
V1.1 Исправление: нулевой указатель, если JAVA_TOOL_OPTIONS не установлен
Пример:
$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
Thread.sleep(600);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
setVisible(true);
Я полагаю, вы действительно хотите не останавливать приложение, а «перезапустить» его. Для этого вы можете использовать это и добавить свой «Сброс» перед сном и после невидимого окна.