我有一个在命令窗口中运行的应用程序。我有Scanner
检测用户何时键入/quit
,并运行相应的quit()
方法。
这很有效,但是有没有办法确定用户是否关闭了控制台而没有输入/quit
,仍然运行quit()
?
答案 0 :(得分:1)
使用关机钩子。仍然没有100%保证它被调用,但它是你可以尝试捕获关闭的最佳方式。一般来说,你应该在关机钩子中做到绝对最小。关闭资源,删除一些日志记录或打印语句,但不要做任何需要很长时间的事情。
我已经复制并稍微修改了此处Detecting when a Java application closes找到的答案,以匹配您的代码。您应该将此代码放在您有权调用quit()的位置。你应该只调用一次,但你应该在整个过程中尽早调用它。
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
quit();
}
});
有关关闭钩子正在做什么的更多详细信息,请参阅此问题:Useful example of a shutdown hook in Java?
请参阅此问题的已接受答案,详细了解关闭何时关闭钩子并且不会被称为shutdown hook vs finalizer method
请参阅此问题,了解您必须使用quit()TL的时间; DR - 取决于How long does the JVM allow shutdown hooks to run before calling halt?
答案 1 :(得分:0)
这是一种风险更高但更强大的方法来捕捉您的程序退出。有几种方法可以故意破坏这种方法,并且有一些摆动空间,操作系统也可能意外破坏它。这就是为什么我说'风险更高'的原因。
您创建一个类Bodyguard,它会找出自己的进程ID,然后以processid作为输入运行RealProgram。然后Bodyguard等待RealProgram关闭,然后终止。同时,RealProgram会监视并检查Bodyguard的进程是否仍在运行。如果它看到Bodyguard已经死亡,那么它将退出其主循环并在终止之前调用quit()。
我在Windows 10上测试了这个,运行Java 8,并从eclipse调试器中杀死了Bodyguard。当Bodyguard被杀死时,RealProgram在调用quit()后关闭。当RealProgram被杀死时,Bodyguard也会终止。
由于我认为你想要使用代码,我已经把一些调试元素留在了那里。
我相信:
保镖类:
package so_38154756;
import java.lang.reflect.InvocationTargetException;
public class Bodyguard {
public static void main(String[] args){
Bodyguard lamb = new Bodyguard();
lamb.initiateSacrifice();
}
private void initiateSacrifice() {
try{
int ownPid = getPID();
runRealProgram(ownPid);
} catch(Exception e){
System.out.println("Sorry, this isn't working.");
}
}
private int getPID() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
int pid = (Integer) pid_method.invoke(mgmt);
return pid;
}
private void runRealProgram(int ownPid) throws Exception {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
ProcessBuilder processBuilder = new ProcessBuilder(path, "-cp", classpath, RealProgram.class.getName(), Integer.valueOf(ownPid).toString());
Process process = processBuilder.start();
process.waitFor();//will wait forever, but will close when the RealProgram closes
}
}
和RealProgram类:
package so_38154756;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Pattern;
public class RealProgram {
private Integer bodyguardPID;
private PrintWriter writer;
public RealProgram(Integer bodyguardPID, PrintWriter writer) {
this.bodyguardPID = bodyguardPID;
this.writer = writer;
}
public static void main(String[] args){
Integer sacrificialPID = Integer.parseInt(args[0]);
try {
PrintWriter writer = new PrintWriter(new File("output.txt"));
RealProgram rp = new RealProgram(sacrificialPID, writer);
writer.println("Past Constructor");
rp.checkToSeeIfBodyguardLives();
writer.println("Bodyguard had died");
writer.close();//yes, yes, this could be slightly prettier with Java 8's try-with-resources
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void checkToSeeIfBodyguardLives() {
while(true){
try { Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//I wait because I was debugging - you may find it wise to not wait
//do whatever it is you're doing
if(!isBodyguardAlive()){
quit();
break;
}
}
}
private boolean isBodyguardAlive() {
try {
String process;
Pattern pattern = Pattern.compile(".*\\\""+bodyguardPID+"\\\".*");//Windows - have tested
//you'll have to figure out your own different pattern for linux - possible source for editing
writer.println("Searching for PID of "+bodyguardPID);
//Process p = Runtime.getRuntime().exec("ps -few"); - Linux - haven't tested - again, possible source for editing
Process p = Runtime.getRuntime().exec(System.getenv("windir") +"\\system32\\"+"tasklist.exe /fo csv /nh");//Windows - have tested
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((process = input.readLine()) != null) {
if(pattern.matcher(process).matches()){
return true;
}
}
input.close();
} catch (Exception err) {
err.printStackTrace();
}
return false;
}
private void quit(){
writer.println("Detected Bodyguard has died");
}
}