簡體   English   中英

使用Runtime.exec()后關閉線程的GUI

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM