[英]Closing a Thread's GUI after using Runtime.exec()
我正在用LibGDX制作游戏,现在我试图通过重新运行jar重新启动游戏,因为我正在使用jar的路径,并通过以下方式找到它:
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString().replace("%20", " ");
使用完之后,我尝试使用Process
和
Runtime.getRuntime().exec("java -jar " + location + "\\\\Test.jar");
现在可以正常工作了,但是问题在于,我用来创建新实例的游戏的第一个实例(从中重新启动)仍然保留在屏幕上,直到第二个实例关闭后才关闭。
这是我的重启代码:
public static void restart() {
Gdx.app.exit();
try {
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString().replace("%20", " ");
System.out.println(location);
Process pro = Runtime.getRuntime().exec(
"java -jar " + location + "\\Test.jar");
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
"reprot.txt")));
InputStream stream = pro.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(
stream));
String line = "";
writer.write(location);
while ((line = reader.readLine()) != null) {
writer.write(line);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
难道我做错了什么? 启动第二个实例后如何关闭游戏的第一个实例?
我尝试使用另一个线程来执行此操作,并包含以下代码:
public static void main(String[] args) {
try {
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString();
System.out.println(location);
Process pro = Runtime.getRuntime().exec(
"java -jar " + location + "\\Test.jar");
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
"report.txt")));
InputStream stream = pro.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(
stream));
String line = "";
writer.write(location);
while ((line = reader.readLine()) != null) {
writer.write(line);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
但是它仍然有同样的问题。
编辑:我试图使用System.exit(0);
,并尝试使用LwjglApplication将其关闭,但它保持不变,但是我取得了一些进展:
创建此新进程时,游戏的第二个实例,第一个实例的UI冻结,导致游戏不响应。 我认为,好吧,如果它不响应,我应该找到一种杀死它并离开另一个实例的方法,但是由于关闭游戏的一个实例(通过强制关闭)这一事实无法实现。 ),您同时关闭两个实例。
我想我想出了一个令人讨厌的难题:
假设我们的主要游戏实例为“ Game_1”,而我们正在创建的实例为“ Game_2”。
在查看了代码并考虑了会发生什么之后(通过测试小类而不是大型游戏),我认为“ Game_1”没有关闭,因为“ Game_2”没有关闭。
用更复杂的术语来说,“ Game_1”的实例不会关闭,因为它以某种方式附加到“ Game_2”,因此在“ Game_2”关闭之前会等待自身关闭。
因此,如果正确,关闭“ Game_1”的方式将使“ Game_2”与“ Game_1”同时运行,从而使其独立,从而允许“ Game_1”继续执行当前的代码进度,这将是Gdx.app.exit();
的实现Gdx.app.exit();
方法。
所以现在问题仍然存在,如何使“ Game_2”的实例独立于“ Game_1”运行? 或者,我将如何使“ Game_1”继续执行代码,或者不等到从“ Game_2”收到退出值。
EDIT2: 大规模进展添加一行代码System.exit(0);
在重新启动类中,“ Game_1”继续不响应, 但是在终止“ Game_1”之后,“ Game_2”没有关闭,我将继续尝试直到找到解决方法。
EDIT3:我继续尝试对其进行修复,以便可以正常工作,但是遇到了另一个问题。 我发现,如果我可以模拟'Game_2'的退出值而不实际退出,那么我可以终止'Game_1'的UI,同时保持游戏2的状态,如果有人有任何想法请与我分享。
EDIT4:我继续尝试执行此操作,但是我无法跟踪正在发生的事情,我正在尝试通过编写将PID传递给重新启动类的方法
"java -cp " + location + "\\\\Test.jar Restart " + PID
但是它似乎不起作用,或者我似乎没有从Restart类接收任何信息(例如,syso)。 最重要的是,我发现我的游戏内存泄漏,一旦解决,就会解决。
请,如果您有任何帮助我的想法,甚至只是理论,请分享一下。
EDIT5:我已经使用此LINK确定了终止给定过程的效率
有一种“更轻松”的方法可以执行您想要的操作。 当然,您将不得不适应自己的应用程序,因为您尝试做的事情完全超出了libgdx的范围。 这是一个跨平台的库,更新/重新启动的想法与移动设备大不相同。
可以在此处找到实际的桌面跨平台解决方案,我强烈建议您不要使用您的方法,因为它不是可靠的解决方案,并且与平台有关。
以下是如何在libgdx中执行此操作的示例。 您需要两件事,启动应用程序的代码和重新启动应用程序的代码。
发射器:
public class TestLauncher {
public static void main(final String[] args) {
final LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "Game";
cfg.width = 1280;
cfg.height = 720;
cfg.backgroundFPS = 12;
cfg.foregroundFPS = 60;
final Runnable rebootable = new Runnable() {
@Override public void run() {
if (Gdx.app != null) {
Gdx.app.exit();
}
TestLauncher.restart();
}
};
new LwjglApplication(new RebootTest(rebootable), cfg);
}
public static void restart() {
final StringBuilder cmd = new StringBuilder();
cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
for (final String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
cmd.append(jvmArg + " ");
}
cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
cmd.append(TestLauncher.class.getName()).append(" ");
try {
Runtime.getRuntime().exec(cmd.toString());
} catch (final IOException e) {
e.printStackTrace();
}
}
}
示例游戏代码:
public class RebootTest implements ApplicationListener {
private final Runnable rebootHook;
private Stage stage;
private Skin skin;
public RebootTest(final Runnable rebootHook) {
this.rebootHook = rebootHook;
}
@Override public void create() {
this.stage = new Stage();
this.skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
final Table table = new Table();
table.setFillParent(true);
final TextButton button = new TextButton("Reboot", this.skin);
button.addListener(new ClickListener() {
@Override public void clicked(final InputEvent event, final float x, final float y) {
Gdx.app.postRunnable(RebootTest.this.rebootHook);
}
});
table.add(button).expand().size(120, 40);
this.stage.addActor(table);
Gdx.input.setInputProcessor(this.stage);
}
@Override public void resize(final int width, final int height) {}
@Override public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
this.stage.act();
this.stage.draw();
}
@Override public void pause() {}
@Override public void resume() {}
@Override public void dispose() {
if (this.stage != null) {
this.stage.dispose();
}
if (this.skin != null) {
this.skin.dispose();
}
}
}
这是解决方案,因为直到明天我才能回答我的问题:
好了,最后,我完成了它的解决,它有几个问题,我只提到其中两个,因为它涉及的是一般代码,而不是我的使用方式。 “ Game_1”将是首先启动的游戏,而“ Game_2”将是重新启动的游戏的实例。 就是这个:
首先,我得到了当前正在运行的当前进程的PID,即“ Game_1”,从中我将创建“ Game_2”。 这样做的问题是Java应用程序都具有相同的名称“ Java.exe”,并且导致一堆具有相同名称的应用程序,因此,我现在添加一条消息,指出游戏应该是唯一的Java实例,而不是日食或类似的东西。
PID检索的代码是这样的:
private static String getPID() {
try {
String line;
Process p = Runtime.getRuntime().exec(
System.getenv("windir") + "\\system32\\" + "tasklist.exe");
BufferedReader input = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
if (line.contains("java")) {
String data = line.subSequence(27, 35).toString();
data = data.trim();
return data;
}
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
return "-1";
}
现在,稍后,我将寻找一种方法来命名当前正在运行的进程,这样您就不必使用line.contains("java")
因为它可能会给出多个实例,但现在它是要多好能有多好。
此代码在Windows内部使用了一个exe文件,该文件基本上提供了计算机上正在运行的所有当前进程,因此您可以找到自己的文件。
返回的列表以以下格式给出:
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
All the processes will be located here.
PID位于第27个字符到第35个字符之间,因此我添加了
String data = line.subSequence(27, 35).toString();
以便它返回进程的PID。
之后,我准备了一个带有执行命令的cmd,如下所示:
String jarLocation = new File(YourClass.class.getProtectedDomain().getCodeSource().getLocation().getPath()).toString();
String command = "java -cp " + jarLocation + " your.Package.here.Restart \""+PID+"\"";
Runtime.getRuntime().exec("cmd /C start cmd.exe /C \"" + command + "\"");
现在首先我得到了.jar文件的位置。 它以以下格式返回:
C:\A%20Folder\To%20YourJar\YourJar.jar
因此,位置必须采用以下格式
jarLocation = jarLocation.replace("%20", " ");
只是将所有%20都转换为空格。
注意如果目录中没有空格,则无需进行格式化的前一步骤。
之后,我准备了实际的命令,如下所示(这是给我的,但是您可以根据需要进行更改)。
java
在cmd中调用Java程序。
-cp
执行位于jar文件内部的类。
然后,我添加了jar位置,然后添加了程序包,并添加了要终止的PID的参数(对于main方法中的String[] args
)。
现在,以下代码行表示一个操作系统依赖性,因此,如果要添加多个操作系统支持,我建议在另一个操作系统中找到与cmd等效的内容,并弄清楚如何使用它。
代码的最后一行是执行,在此我们获得运行时,启动cmd并在关闭cmd之前执行一条命令。
您可以在以下问题中找到有关它的详细信息: LINK
@Vincent Ramdhanie还提供了指向激活cmd时可以使用运行时运行的命令的链接。
之后,我有一个实际上是在重新启动游戏的类,称为Restart。
像最后一行代码一样,其中的一行代码表示操作系统依赖性,因此,如果要支持多个操作系统, taskkil
在其他操作系统中找到与taskkil
等效的代码。 据@erhun它pkill
为Linux或什么的,对不起,我不完全记得。
这是该类的代码:
public static void main(String[] args) {
try {
String location = new File(DesktopLauncher.class
.getProtectionDomain().getCodeSource().getLocation()
.getPath()).toString();
location = "\"" + location.replaceAll("%20", " ");
Runtime.getRuntime().exec("taskkill /F /PID " + args[0]);
Runtime.getRuntime().exec("java -jar " + location);
} catch (IOException e) {
e.printStackTrace();
}
}
与上一行相同,此处的位置与以前相同,如果目录中有空格,则必须对其进行格式化。
之后,您需要终止先前的过程,即taskkill /F /PID + args[0]
进入的位置。如果运行,您将终止ID为args [0]的任务,该ID为'Game_1'' PID。
之后,我只运行了jar文件,您一切顺利。
我想说明一下,我尝试运行它,以便主类(DesktopLauncher)在运行时通过exec命令使用Restart类,但是问题仍然存在,并且我发现解决此问题的唯一方法是正常工作在它周围,并使用cmd。 (这是在使用cmd调试位置字符串之后)。
就是这样,我工作了整整一周的时间,试图解决这个问题,但就目前而言,这虽然很粗糙,但它是一个解决方案。 如果我在此代码中的某处存在问题,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.