Як я можу перезапустити програму 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
. На оракулі 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, і якщо програма eclipse java завершується за допомогою коду перезапуску, 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);
}
/ min, щоб запустити скрипт у згорнутому вікні
^ & вихід, щоб закрити вікно 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).
Кожного разу, коли програму потрібно перезапустити, вона повинна викликати запуск методу та вийти з віртуальної машини. У завданні Java Ant має бути встановлено параметри fork і spawn на 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);
Думаю, ви насправді не хочете зупиняти програму, а «Перезапустити» її. Для цього ви можете використати це і додати свій "Скинути" перед сном і після невидимого вікна.