Làm cách nào để khởi động lại ứng dụng Java AWT? Tôi có một nút mà tôi đã đính kèm một trình xử lý sự kiện. Tôi nên sử dụng mã nào để khởi động lại ứng dụng?
Tôi muốn làm điều tương tự Application.Restart()
như trong ứng dụng C #.
Làm cách nào để khởi động lại ứng dụng Java AWT? Tôi có một nút mà tôi đã đính kèm một trình xử lý sự kiện. Tôi nên sử dụng mã nào để khởi động lại ứng dụng?
Tôi muốn làm điều tương tự Application.Restart()
như trong ứng dụng C #.
Câu trả lời:
Tất nhiên, có thể khởi động lại một ứng dụng Java.
Phương pháp sau đây cho thấy một cách để khởi động lại một ứng dụng 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);
}
Về cơ bản nó làm như sau:
MyClassInTheJar
lớp để tự tìm vị trí jar)System.exit(0)
chấm dứt tiến trình con có câu trả lời giống như câu trả lời này có thực sự hoạt động hay không và tại sao. Nếu bạn không thể đưa ra lời giải thích hợp lý cùng với câu trả lời của mình, bạn đã làm một công việc không tốt. Câu trả lời cung cấp nhiều câu hỏi hơn câu trả lời không phải là một ví dụ về câu trả lời thấu đáo. Câu trả lời tốt không chỉ hiển thị mã mà còn giải thích cách thức và lý do tại sao chúng hoạt động, nhược điểm là gì và đâu là lựa chọn thay thế. Bạn thậm chí đã không cố gắng che những thứ này.
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);
}
}
Dành riêng cho tất cả những ai nói rằng điều đó là không thể.
Chương trình này thu thập tất cả thông tin có sẵn để xây dựng lại dòng lệnh ban đầu. Sau đó, nó khởi chạy nó và vì nó là lệnh giống nhau nên ứng dụng của bạn sẽ khởi động lần thứ hai. Sau đó, chúng tôi thoát khỏi chương trình gốc, chương trình con vẫn chạy (ngay cả trong Linux) và thực hiện điều tương tự.
CẢNH BÁO : Nếu bạn chạy điều này, hãy lưu ý rằng nó không bao giờ kết thúc việc tạo các quy trình mới, tương tự như một quả bom ngã ba .
ManagementFactory.getRuntimeMXBean().getInputArguments()
sẽ chỉ cung cấp cho bạn các đối số đầu vào được chuyển đến JVM. Nó bỏ lỡ các thông số được chuyển đến ứng dụng của bạn. vd java -jar start.jar -MISSED_PARAM=true
. Trên một jvm oracle, bạn có thể lấy các tham số đó bằng cách sử dụng System.getProperty("sun.java.command")
.
ProcessBuilder
và inheritIO()
, máy ảo con có thể được khởi động theo cách mà máy ảo mẹ sẽ kết thúc.
Về cơ bản, bạn không thể. Ít nhất là không theo một cách đáng tin cậy. Tuy nhiên, bạn không cần phải làm vậy.
Để khởi động lại chương trình Java, bạn cần khởi động lại JVM. Để khởi động lại JVM, bạn cần
Định vị java
trình khởi chạy đã được sử dụng. Bạn có thể thử với System.getProperty("java.home")
nhưng không có gì đảm bảo rằng điều này sẽ thực sự trỏ đến trình khởi chạy được sử dụng để khởi chạy ứng dụng của bạn. (Giá trị trả về có thể không trỏ đến JRE được sử dụng để khởi chạy ứng dụng hoặc nó có thể đã bị ghi đè -Djava.home
.)
Bạn có lẽ sẽ muốn tôn vinh những bộ nhớ ban đầu cài đặt vv ( -Xmx
, -Xms
, ...), do đó bạn cần phải tìm ra những nơi sử dụng để khởi động JVM đầu tiên. Bạn có thể thử sử dụng ManagementFactory.getRuntimeMXBean().getInputArguments()
nhưng không có gì đảm bảo rằng điều này sẽ phản ánh các cài đặt được sử dụng. Điều này thậm chí còn được viết trong tài liệu của phương pháp đó:
Thông thường, không phải tất cả các tùy chọn dòng lệnh cho lệnh 'java' đều được chuyển đến máy ảo Java. Do đó, các đối số đầu vào được trả về có thể không bao gồm tất cả các tùy chọn dòng lệnh.
Nếu chương trình của bạn đọc đầu vào từ Standard.in
stdin ban đầu sẽ bị mất khi khởi động lại.
Rất nhiều thủ thuật và hack này sẽ thất bại khi có a SecurityManager
.
Tôi khuyên bạn nên thiết kế ứng dụng của mình sao cho dễ dàng dọn dẹp mọi thứ và sau đó tạo một phiên bản mới của lớp "chính" của bạn.
Nhiều ứng dụng được thiết kế để không làm gì khác ngoài việc tạo một thể hiện trong phương thức main:
public class MainClass {
...
public static void main(String[] args) {
new MainClass().launch();
}
...
}
Bằng cách sử dụng mẫu này, nó sẽ đủ dễ dàng để thực hiện một số việc như:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
và launch()
trả về true nếu và chỉ khi ứng dụng bị tắt theo cách cần khởi động lại.
Nói một cách chính xác, một chương trình Java không thể tự khởi động lại vì để làm như vậy, nó phải giết JVM mà nó đang chạy và sau đó khởi động lại, nhưng một khi JVM không còn chạy nữa (bị giết) thì không thể thực hiện hành động nào.
Bạn có thể thực hiện một số thủ thuật với trình tải lớp tùy chỉnh để tải, đóng gói và khởi động lại các thành phần AWT nhưng điều này có thể sẽ gây ra nhiều vấn đề đau đầu liên quan đến vòng lặp sự kiện GUI.
Tùy thuộc vào cách ứng dụng được khởi chạy, bạn có thể bắt đầu JVM trong một tập lệnh trình bao bọc có chứa vòng lặp do / while, vòng lặp này tiếp tục trong khi JVM thoát bằng một mã cụ thể, sau đó ứng dụng AWT sẽ phải gọi System.exit(RESTART_CODE)
. Ví dụ, trong mã giả tập lệnh:
DO
# Launch the awt program
EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)
Ứng dụng AWT sẽ thoát khỏi JVM bằng thứ gì đó khác với RESTART_CODE khi kết thúc "bình thường" không yêu cầu khởi động lại.
JavaApplicationStub
... Không chắc liệu có một cách dễ dàng để giải quyết vấn đề đó hay không.
Eclipse thường khởi động lại sau khi cài đặt plugin. Họ thực hiện việc này bằng cách sử dụng trình bao bọc eclipse.exe (ứng dụng trình khởi chạy) cho các cửa sổ. Ứng dụng này thực thi jar người chạy nhật thực lõi và nếu ứng dụng java nhật thực kết thúc bằng mã khởi chạy lại, eclipse.exe khởi động lại bàn làm việc. Bạn có thể tạo một bit tương tự của mã gốc, tập lệnh shell hoặc một trình bao bọc mã java khác để khởi động lại.
các cửa sổ
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);
}
/ phút để bắt đầu tập lệnh trong cửa sổ thu nhỏ
^ & exit để đóng cửa sổ cmd sau khi kết thúc
một tập lệnh cmd mẫu có thể là
@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
ngủ 10 giấc ngủ trong 10 giây
Nếu bạn thực sự cần khởi động lại ứng dụng của mình, bạn có thể viết một ứng dụng riêng khi khởi động nó ...
Trang này cung cấp nhiều ví dụ khác nhau cho các tình huống khác nhau:
Mặc dù câu hỏi này đã cũ và đã được trả lời, nhưng tôi đã tình cờ gặp vấn đề với một số giải pháp và quyết định thêm đề xuất của mình vào hỗn hợp.
Vấn đề với một số giải pháp là chúng xây dựng một chuỗi lệnh duy nhất. Điều này tạo ra các vấn đề khi một số tham số chứa khoảng trắng, đặc biệt là java.home .
Ví dụ: trên windows, dòng
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Có thể trả lại một cái gì đó như thế này:C:\Program Files\Java\jre7\bin\java
Chuỗi này phải được đặt trong dấu ngoặc kép hoặc thoát do khoảng trống trong Program Files
. Không phải là một vấn đề lớn, nhưng hơi khó chịu và dễ xảy ra lỗi, đặc biệt là trong các ứng dụng đa nền tảng.
Do đó, giải pháp của tôi xây dựng lệnh dưới dạng một mảng lệnh:
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();
}
}
Tôi đang tự nghiên cứu chủ đề này khi bắt gặp câu hỏi này.
Bất kể thực tế là câu trả lời đã được chấp nhận, tôi vẫn muốn đưa ra một cách tiếp cận thay thế để hoàn thiện. Cụ thể, Apache Ant là một giải pháp rất linh hoạt.
Về cơ bản, mọi thứ tổng hợp thành một tệp kịch bản Ant với một tác vụ thực thi Java duy nhất (tham khảo tại đây và tại đây ) được gọi từ mã Java (xem tại đây ). Mã Java này, có thể là một phương thức khởi chạy , có thể là một phần của ứng dụng cần được khởi động lại. Ứng dụng cần có sự phụ thuộc vào thư viện Apache Ant (jar).
Bất cứ khi nào ứng dụng cần được khởi động lại, nó sẽ gọi phương thức khởi chạy và thoát khỏi máy ảo. Nhiệm vụ Ant java nên có các tùy chọn fork và spawn được đặt thành true.
Đây là một ví dụ về tập lệnh 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>
Mã cho phương thức khởi chạy có thể trông giống như sau:
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);
}
Một điều rất tiện lợi ở đây là cùng một tập lệnh được sử dụng để khởi động ứng dụng ban đầu cũng như khởi động lại.
Chỉ thêm thông tin không có trong các câu trả lời khác.
/proc/self/cmdline
Nếu bạn đang chạy trong môi trường cung cấp procfs và do đó có /proc
sẵn hệ thống tệp (có nghĩa là đây không phải là giải pháp di động), bạn có thể đọc Java /proc/self/cmdline
để tự khởi động lại, như sau:
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();
}
}
Trên các hệ thống có /proc/self/cmdline
sẵn, đây có lẽ là cách thanh lịch nhất để "khởi động lại" tiến trình Java hiện tại từ Java. Không có JNI tham gia và không cần đoán đường dẫn và nội dung. Điều này cũng sẽ xử lý tất cả các tùy chọn JVM được chuyển đến java
nhị phân. Dòng lệnh sẽ hoàn toàn giống với dòng lệnh của quy trình JVM hiện tại.
Nhiều hệ thống UNIX bao gồm GNU / Linux (bao gồm cả Android) ngày nay có procfs Tuy nhiên, trên một số hệ thống như FreeBSD, nó không được dùng nữa và bị loại bỏ dần. Mac OS X là một ngoại lệ vì nó không có procfs . Windows cũng không có procfs . Cygwin có procfs nhưng nó vô hình với Java vì nó chỉ hiển thị với các ứng dụng sử dụng Cygwin DLL thay vì lệnh gọi hệ thống Windows và Java không biết về Cygwin.
ProcessBuilder.inheritIO()
Mặc định là stdin
/ stdout
/ stderr
(trong Java được gọi là System.in
/ System.out
/ System.err
) của Quy trình đã bắt đầu được đặt thành các đường ống cho phép quy trình hiện đang chạy giao tiếp với quy trình mới bắt đầu. Nếu bạn muốn khởi động lại quy trình hiện tại, rất có thể đây không phải là điều bạn muốn . Thay vào đó, bạn muốn rằng stdin
/ stdout
/ stderr
giống với / của VM hiện tại. Điều này được gọi là kế thừa . Bạn có thể làm như vậy bằng cách gọi phiên bản inheritIO()
của bạn ProcessBuilder
.
Trường hợp sử dụng thường xuyên của một restart()
chức năng là khởi động lại ứng dụng sau khi cập nhật. Lần cuối cùng tôi thử điều này trên Windows, điều này có vấn đề. Khi ghi đè .jar
tệp của ứng dụng bằng phiên bản mới, ứng dụng bắt đầu hoạt động sai và đưa ra các ngoại lệ về .jar
tệp. Tôi chỉ nói, trong trường hợp đây là trường hợp sử dụng của bạn. Trước đó, tôi đã giải quyết vấn đề bằng cách gói ứng dụng trong một tệp lô và sử dụng giá trị trả về kỳ diệu System.exit()
mà từ đó tôi đã truy vấn trong tệp lô và thay vào đó, tệp lô sẽ khởi động lại ứng dụng.
Câu hỏi cũ và tất cả những điều đó. Nhưng đây là một cách khác mang lại một số lợi thế.
Trên Windows, bạn có thể yêu cầu bộ lập lịch tác vụ khởi động lại ứng dụng cho bạn. Điều này có lợi thế là đợi một khoảng thời gian cụ thể trước khi ứng dụng được khởi động lại. Bạn có thể đi tới trình quản lý tác vụ và xóa tác vụ và nó sẽ ngừng lặp lại.
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);
Tương tự như câu trả lời ' cải tiến ' của Yoda , nhưng với những cải tiến hơn nữa (cả chức năng, khả năng đọc và khả năng kiểm tra). Bây giờ nó an toàn để chạy và khởi động lại nhiều lần với số lượng đối số chương trình đã cho.
JAVA_TOOL_OPTIONS
tùy chọn.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 Sửa lỗi: con trỏ null nếu JAVA_TOOL_OPTIONS không được đặt
Thí dụ:
$ 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);
Tôi đoán bạn không thực sự muốn dừng ứng dụng, nhưng để "Khởi động lại" nó. Đối với điều đó, bạn có thể sử dụng điều này và thêm "Đặt lại" của mình trước khi ngủ và sau cửa sổ ẩn.