JavaFX 2.1和线程? - 或 - 优雅地结束JavaFX应用程序?

5 java multithreading exit javafx-2

我想要一种从Java应用程序播放mp3文件的简单方法.经过一些研究后,我发现最新版本的Java 7 SE与JavaFX打包在一起,所以我想我会尝试.这个问题不是关于播放mp3文件,而是关于让JavaFX以一种良好的方式工作.

因此,在我第一次使用JavaFX进行的实验中,我使用了一些建议在stackoverflow中发布的代码(参见此处)并基本上创建了以下测试程序:

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

public class progtest extends Application
{

    /*---------------------------------------------------------*\
        FUNCTION:  main()
    \*---------------------------------------------------------*/
    public static void main(String args[])
    {

        launch(args);

    } /* END:  main() */

    /*---------------------------------------------------------*\
        FUNCTION:  start()
    \*---------------------------------------------------------*/
    @Override
    public void start(Stage stage)
    {

        Media medMsg 
            = 
            new Media(getClass().getResource("msg.mp3").toExternalForm());
        MediaPlayer medplMsg = new MediaPlayer(medMsg);
        medplMsg.play();

        System.out.println("Here.\n");

    } /* END:  start() */

}
Run Code Online (Sandbox Code Playgroud)

(这比我原来的测试程序稍微复杂一点:这个版本是在jewelsea提出的建议之后发出的,这是为了回应我之前发布的关于让程序运行的问题(见这里).)

要编译此代码我使用:

javac -cp "c:\Program Files\Oracle\JavaFX 2.1 Runtime\lib\jfxrt.jar";..\bin -d ..\bin ..\src\progtest.java
Run Code Online (Sandbox Code Playgroud)

并且,为了运行代码,我进入了我的..\bin目录并使用:

java -cp .;"c:\Program Files\Oracle\JavaFX 2.1 Runtime\lib\jfxrt.jar" progtest
Run Code Online (Sandbox Code Playgroud)

该程序运行并播放剪辑播放.但是,除非我执行Ctrl-C,否则程序不会返回到命令提示符.

这种行为似乎是某种线程问题.我曾经多次使用Java线程看过这种行为(遗憾的是,它存在真正的问题,允许程序优雅退出).

所以,我想,也许如果我把代码放在自己的线程中然后在该线程结束时结束程序的主线程,那么事情就没问题了.(我可以看出这并不完全是一个有说服力的想法,但无论如何,我想我会尝试.)所以,我编写了以下第二版代码:

这是主线程:

import javafx.application.Application;
import javafx.stage.Stage;
import Msg.*;

public class progtest2 extends Application
{

    /*---------------------------------------------------------*\
        FUNCTION:  main()
    \*---------------------------------------------------------*/
    public static void main(String args[])
    {

        launch(args);

    } /* END:  main() */

    /*---------------------------------------------------------*\
        FUNCTION:  start()
    \*---------------------------------------------------------*/
    @Override
    public void start(Stage stage)
    {

        Msg msgTime = new Msg();
        msgTime.passClass(getClass());
        msgTime.start();

        try
        {
            msgTime.join();
        }
        catch (InterruptedException e)
        {
        }

        System.out.println("Here.\n");

    } /* END:  start() */

}
Run Code Online (Sandbox Code Playgroud)

这里是应该实际播放mp3文件的Msg线程的代码:

package Msg;

import KeyIO.*;
import javafx.application.Application;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

public class Msg extends Thread
{

    private Class classRt = null;

    /*--------------------------------------------------------*\
        FUNCTION:  passClass()
    \*--------------------------------------------------------*/
    public void passClass(Class rt)
    {

        classRt = rt;

    } /* END:  passClass() */

    /*--------------------------------------------------------*\
        FUNCTION:  run()
    \*--------------------------------------------------------*/
    public void run()
    {

        Media medMsg
            = new Media(classRt.getResource("msg.mp3").toExternalForm());
        MediaPlayer medplMsg = new MediaPlayer(medMsg);
        medplMsg.play();

        System.out.println("Leaving Msg thread.\n");

    } /* END:  run() */

}
Run Code Online (Sandbox Code Playgroud)

(我使用相同的命令构建并运行这些文件(比照文件名和类名),如上所述.)

该程序运行并播放mp3文件.在听到该文件之前,您会看到"离开Msg线程".然后是"Here.",你可以看到它分别来自Msg线程和主线程.但是,该计划再次没有结束.我必须按Ctrl-C才能返回命令提示符.

我在主线程的末尾放了一个System.exit()调用,只是为了看看行为是什么.行为是没有听到剪辑.看起来System.exit()调用在剪辑播放之前结束了该过程.

所以,接下来我尝试将以下几行放在System.out.println("Here.\n")之间; 和我做的System.exit()调用:

System.out.print("Press ENTER to end...");
KeyIO.ReadLine();
Run Code Online (Sandbox Code Playgroud)

(KeyIO是我专门为键盘输入输出而制作的一个类.这里的细节并不重要.)

我认为这只会阻止主线程直到剪辑播放为止.

但不是.相反,剪辑没有播放.当我按下ENTER键时,程序退出而不播放剪辑.

好的,现在我把键盘输入阻塞代码留在了,但是我拿出了System.exit()调用.现在播放了音频剪辑,但程序没有再次结束.

Urgh!

任何想法如何优雅地让它以某种显而易见的方式工作会想要它起作用?我只想打电话来播放剪辑,然后我希望能够结束该程序.

先感谢您!

jew*_*sea 7

在第一个progtest类中,您不创建窗口,因此不会调用关闭最后一个窗口的implicitExit操作.这意味着程序将一直运行,直到您调用应用程序的退出方法,而您没有这样做.

要在mp3播放完毕后让程序退出,请调用mediaPlayer.setOnEndOfMedia(runnable)并在runnable回调中调用Platform.exit()方法.

medplMsg.setOnEndOfMedia(new Runnable() {
  @Override public void run() {
    Platform.exit();
  }
});
Run Code Online (Sandbox Code Playgroud)

您的问题中的所有其他线程内容都不是必需的(并且无论如何都无法解决您的问题......).我不建议使用自定义线程,除非你真的需要它(你没有).

JavaFX 2.2添加了以下api:

Platform.setImplicitExit(boolean implicitExit)
Run Code Online (Sandbox Code Playgroud)

将implicitExit属性设置为指定的值.如果此属性为true,则JavaFX运行时将在最后一个窗口关闭时隐式关闭; JavaFX启动程序将调用Application.stop()方法并终止JavaFX应用程序线程.如果此属性为false,则应用程序将在最后一个窗口关闭后继续正常运行,直到应用程序调用exit().默认值是true.

这应该会给你一些关于你遇到的问题的线索.