Jak mogę zrestartować aplikację Java AWT? Mam przycisk, do którego dołączyłem procedurę obsługi zdarzeń. Jakiego kodu należy użyć, aby zrestartować aplikację?
Chcę zrobić to samo, Application.Restart()
co w aplikacji C #.
Jak mogę zrestartować aplikację Java AWT? Mam przycisk, do którego dołączyłem procedurę obsługi zdarzeń. Jakiego kodu należy użyć, aby zrestartować aplikację?
Chcę zrobić to samo, Application.Restart()
co w aplikacji C #.
Odpowiedzi:
Oczywiście istnieje możliwość ponownego uruchomienia aplikacji Java.
Poniższa metoda przedstawia sposób ponownego uruchomienia aplikacji 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);
}
Zasadniczo wykonuje następujące czynności:
MyClassInTheJar
klasy, aby znaleźć samą lokalizację jar)System.exit(0)
kończy proces potomny, ma taką samą odpowiedź, jak to, czy ta odpowiedź naprawdę działa i dlaczego. Jeśli nie możesz podać sensownego wyjaśnienia wraz z odpowiedzią, wykonałeś złą robotę. Odpowiedź, która dostarcza więcej pytań niż odpowiedzi, nie jest przykładem dokładnej odpowiedzi. Dobre odpowiedzi nie tylko pokazują kod, ale także wyjaśniają, jak i dlaczego działają, jakie są wady i jakie są alternatywy. Nawet nie próbowałeś tego ukrywać.
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);
}
}
Dedykowany wszystkim, którzy mówią, że to niemożliwe.
Ten program gromadzi wszystkie dostępne informacje w celu odtworzenia oryginalnego wiersza poleceń. Następnie uruchamia je, a ponieważ jest to to samo polecenie, aplikacja uruchamia się po raz drugi. Następnie wychodzimy z oryginalnego programu, program potomny działa (nawet pod Linuksem) i robi to samo.
OSTRZEŻENIE : Jeśli to uruchomisz, pamiętaj, że nigdy nie kończy się tworzenie nowych procesów, podobnie jak bomba widelca .
ManagementFactory.getRuntimeMXBean().getInputArguments()
podadzą tylko argumenty wejściowe przekazane do maszyny JVM. Pomija parametry przekazane do Twojej aplikacji. np java -jar start.jar -MISSED_PARAM=true
. W jvm Oracle można pobrać te parametry za pomocą System.getProperty("sun.java.command")
.
ProcessBuilder
i inheritIO()
, podrzędną maszynę wirtualną można uruchomić w taki sposób, że nadrzędna maszyna wirtualna zakończy się.
Zasadniczo nie możesz. Przynajmniej nie w rzetelny sposób. Jednak nie powinieneś tego robić.
Aby zrestartować program Java, należy zrestartować maszynę JVM. Aby zrestartować maszynę JVM, musisz
Znajdź java
używany program uruchamiający. Możesz spróbować, System.getProperty("java.home")
ale nie ma gwarancji, że wskaże to program uruchamiający, który został użyty do uruchomienia aplikacji. (Zwracana wartość może nie wskazywać na środowisko JRE używane do uruchamiania aplikacji lub mogła zostać zastąpiona przez -Djava.home
.)
Ty przypuszczalnie chcą uhonorować oryginalną pamięć ustawienia etc ( -Xmx
, -Xms
...), więc trzeba dowiedzieć się, które ustawienia gdzie użyty do uruchomienia pierwszego JVM. Możesz spróbować użyć, ManagementFactory.getRuntimeMXBean().getInputArguments()
ale nie ma gwarancji, że będzie to odzwierciedlać użyte ustawienia. Jest to nawet zapisane w dokumentacji tej metody:
Zazwyczaj nie wszystkie opcje wiersza polecenia polecenia „java” są przekazywane do wirtualnej maszyny języka Java. W związku z tym zwrócone argumenty wejściowe mogą nie zawierać wszystkich opcji wiersza polecenia.
Jeśli program odczyta dane wejściowe z Standard.in
oryginalnego standardowego wejścia, zostanie utracone podczas ponownego uruchamiania.
Wiele z tych sztuczek i hacków zawiedzie w obecności pliku SecurityManager
.
Zalecam zaprojektowanie aplikacji tak, aby można było łatwo wszystko uporządkować, a następnie utworzyć nową instancję swojej „głównej” klasy.
Wiele aplikacji nie robi nic poza tworzeniem instancji w metodzie głównej:
public class MainClass {
...
public static void main(String[] args) {
new MainClass().launch();
}
...
}
Korzystając z tego wzorca, powinno być łatwo zrobić coś takiego:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
i niech launch()
zwraca prawdę wtedy i tylko wtedy, gdy aplikacja została zamknięta w taki sposób, że trzeba ją ponownie uruchomić.
Ściśle mówiąc, program Java nie może sam się zrestartować, ponieważ w tym celu musi zabić maszynę JVM, w której działa, a następnie uruchomić ją ponownie, ale gdy maszyna JVM przestanie działać (zostanie zabita), nie można podjąć żadnych działań.
Możesz zrobić kilka sztuczek z niestandardowymi programami ładującymi klasy, aby załadować, spakować i ponownie uruchomić komponenty AWT, ale prawdopodobnie spowoduje to wiele problemów związanych z pętlą zdarzeń GUI.
W zależności od sposobu uruchomienia aplikacji można uruchomić JVM w skrypcie opakowującym zawierającym pętlę do / while, która jest kontynuowana, gdy JVM kończy pracę z określonym kodem, wtedy aplikacja AWT musiałaby wywołać System.exit(RESTART_CODE)
. Na przykład w pseudokodzie skryptowym:
DO
# Launch the awt program
EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)
Aplikacja AWT powinna opuścić maszynę JVM za pomocą czegoś innego niż RESTART_CODE po „normalnym” zakończeniu, które nie wymaga ponownego uruchomienia.
JavaApplicationStub
... Nie jestem pewien, czy jest na to łatwy sposób.
Eclipse zwykle uruchamia się ponownie po zainstalowaniu wtyczki. Robią to za pomocą opakowania eclipse.exe (aplikacji uruchamiającej) dla systemu Windows. Ta aplikacja wykonuje rdzeń eclipse runner jar i jeśli aplikacja eclipse java zakończy działanie z ponownym uruchomieniem kodu, eclipse.exe restartuje środowisko robocze. Możesz zbudować podobny fragment kodu natywnego, skryptu powłoki lub innego opakowania kodu java, aby zrestartować.
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, aby uruchomić skrypt w zminimalizowanym oknie
^ & wyjdź, aby zamknąć okno cmd po zakończeniu
przykładowy skrypt cmd może być
@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
spać 10 spać przez 10 sekund
Jeśli naprawdę potrzebujesz ponownie uruchomić aplikację, możesz napisać oddzielną aplikację i ją uruchomić ...
Ta strona zawiera wiele różnych przykładów dla różnych scenariuszy:
Chociaż to pytanie jest stare i odpowiedziałem, natknąłem się na problem z niektórymi rozwiązaniami i postanowiłem dodać moją sugestię do miksu.
Problem z niektórymi rozwiązaniami polega na tym, że tworzą one pojedynczy ciąg poleceń. Stwarza to problemy, gdy niektóre parametry zawierają spacje, zwłaszcza java.home .
Na przykład w systemie Windows linia
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Może zwrócić coś takiego:C:\Program Files\Java\jre7\bin\java
Ten ciąg musi być zawinięty w cudzysłów lub zmieniony ze względu na spację w Program Files
. Nie jest to duży problem, ale nieco denerwujący i podatny na błędy, szczególnie w aplikacjach wieloplatformowych.
Dlatego moje rozwiązanie buduje polecenie jako tablicę poleceń:
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();
}
}
Wystarczy dodać informacje, których nie ma w innych odpowiedziach.
/proc/self/cmdline
jest dostępnyJeśli pracujesz w środowisku, które zapewnia procfs i dlatego masz /proc
dostępny system plików (co oznacza, że nie jest to rozwiązanie przenośne), możesz odczytać Javę /proc/self/cmdline
w celu ponownego uruchomienia, na przykład:
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();
}
}
W systemach z /proc/self/cmdline
dostępnością jest to prawdopodobnie najbardziej elegancki sposób „ponownego uruchomienia” bieżącego procesu Java z poziomu Javy. Bez JNI i bez zgadywania ścieżek i innych rzeczy. To również zajmie się wszystkimi opcjami JVM przekazanymi do java
pliku binarnego. Wiersz poleceń będzie dokładnie taki sam, jak w bieżącym procesie JVM.
Wiele systemów UNIX, w tym GNU / Linux (w tym Android), ma obecnie procfs. Jednak w niektórych, takich jak FreeBSD, jest on przestarzały i wycofywany. Mac OS X jest wyjątkiem w tym sensie, że nie ma procfs . Windows również nie ma procfs . Cygwin ma procfs, ale jest niewidoczny dla Javy, ponieważ jest widoczny tylko dla aplikacji używających bibliotek DLL Cygwin zamiast wywołań systemowych Windows, a Java nie wie o Cygwin.
ProcessBuilder.inheritIO()
Domyślnie stdin
/ stdout
/ stderr
(w Javie wywoływane System.in
/ System.out
/ System.err
) uruchomionego procesu są ustawione na potoki, które umożliwiają bieżącemu procesowi komunikację z nowo uruchomionym procesem. Jeśli chcesz ponownie uruchomić bieżący proces, najprawdopodobniej nie jest to to, czego chcesz . Zamiast tego chciałbyś, aby stdin
/ stdout
/ stderr
były takie same jak te z bieżącej maszyny wirtualnej. Nazywa się to dziedziczeniem . Możesz to zrobić, wywołując inheritIO()
swoją ProcessBuilder
instancję.
Częstym przypadkiem użycia restart()
funkcji jest ponowne uruchomienie aplikacji po aktualizacji. Kiedy ostatnio próbowałem tego w systemie Windows, było to problematyczne. Po nadpisaniu .jar
pliku aplikacji nową wersją, aplikacja zaczęła działać nieprawidłowo i dawała wyjątki dotyczące .jar
pliku. Mówię tylko, na wypadek, gdyby to był twój przypadek użycia. Wtedy rozwiązałem problem, opakowując aplikację w plik wsadowy i używając magicznej wartości zwracanej, z System.exit()
której odpytałem w pliku wsadowym i zamiast tego plik wsadowy ponownie uruchomił aplikację.
Kiedy natknąłem się na to pytanie, sam badałem ten temat.
Niezależnie od tego, że odpowiedź została już zaakceptowana, nadal chciałbym zaproponować alternatywne podejście do kompletności. W szczególności Apache Ant służył jako bardzo elastyczne rozwiązanie.
Zasadniczo wszystko sprowadza się do pliku skryptu Ant z pojedynczym zadaniem wykonania Java (patrz tutaj i tutaj ) wywoływanym z kodu Java (patrz tutaj ). Ten kod Java, który może być uruchomieniem metody , może być częścią aplikacji, która wymaga ponownego uruchomienia. Aplikacja musi mieć zależność od biblioteki Apache Ant (jar).
Zawsze, gdy aplikacja wymaga ponownego uruchomienia, powinna wywołać uruchomienie metody i zamknąć maszynę wirtualną. Zadanie Ant Java powinno mieć opcje fork i spawn ustawione na true.
Oto przykład skryptu 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>
Kod metody uruchamiania może wyglądać mniej więcej tak:
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);
}
Bardzo wygodną rzeczą jest to, że ten sam skrypt jest używany do początkowego uruchamiania aplikacji, jak również do ponownego uruchamiania.
Stare pytanie i tak dalej. Ale to kolejny sposób, który ma pewne zalety.
W systemie Windows możesz poprosić harmonogram zadań o ponowne uruchomienie aplikacji. Ma to tę zaletę, że czeka określony czas, zanim aplikacja zostanie ponownie uruchomiona. Możesz przejść do menedżera zadań i usunąć zadanie, a ono przestaje się powtarzać.
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);
Podobna do „ ulepszonej ” odpowiedzi Yody , ale z dalszymi ulepszeniami (zarówno funkcjonalnymi, czytelnymi, jak i testowalnymi). Teraz można go bezpiecznie uruchomić i zrestartować tyle razy, ile podano argumentów programu.
JAVA_TOOL_OPTIONS
opcji.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 Bugfix: pusty wskaźnik, jeśli JAVA_TOOL_OPTIONS nie jest ustawiony
Przykład:
$ 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);
Wydaje mi się, że tak naprawdę nie chcesz zatrzymywać aplikacji, ale ją „uruchomić ponownie”. W tym celu możesz użyć tego i dodać swój „Reset” przed snem i po niewidzialnym oknie.