You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX重复调用stage.show()与mediaPlayer.play()失效问题求助

问题:JavaFX通知首次正常,后续重复触发失效

我需要在Windows右下角显示带动画和音效的通知,首次启动Test.main()时功能完全正常,但后续通过定时任务重复触发播放动画和音效时失效。相关代码如下,求排查解决:


Test.java

public class Test {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
        executor.scheduleAtFixedRate(new NotificationRunnable(), 0, 2 * 60, TimeUnit.SECONDS);

        executor.scheduleAtFixedRate(new NotificationRunnable(), 0, 4 * 60, TimeUnit.SECONDS);

        executor.scheduleAtFixedRate(new NotificationRunnable(), 0, 9 * 60, TimeUnit.SECONDS);

        executor.scheduleAtFixedRate(new NotificationRunnable(), 0, 13 * 60, TimeUnit.SECONDS);
       
        //Launching application first time
        NotificationApp.runFromScheduledExecutor();
        
    }
}

NotificationRunnable.java

public class NotificationRunnable implements Runnable{
    @Override
    public void run() {
        Platform.runLater(() -> {
            NotificationApp.playAnimation();
        });
        System.out.println("Done");
    }
}

NotificationApp.java

public class NotificationApp extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    static Stage primaryStage = null;
    static MediaPlayer mediaPlayer = null;

    @Override
    public void start(Stage primaryStage) {
        // Create a custom dialog with an ImageView
        Image gifImage = new Image(String.valueOf(new File("img/giphy.gif"))); // Replace with your GIF file path
        ImageView gifImageView = new ImageView(gifImage);
        Pane pane = new Pane(gifImageView);
        Scene scene = new Scene(pane);

        // Set stage properties
        primaryStage.initStyle(StageStyle.TRANSPARENT); // Hide the window decoration
        primaryStage.setAlwaysOnTop(true);

        // Determine screen dimensions
        double screenWidth = Screen.getPrimary().getVisualBounds().getWidth();
        double screenHeight = Screen.getPrimary().getVisualBounds().getHeight();
        double stageWidth = gifImage.getWidth();
        double stageHeight = gifImage.getHeight();

        // Set the stage position to the right corner
        double xOffset = screenWidth - stageWidth;
        double yOffset = screenHeight - stageHeight;
        primaryStage.setX(xOffset);
        primaryStage.setY(yOffset);

        primaryStage.setScene(scene);
        this.primaryStage = primaryStage;

        Media sound = new Media(new Test().getClass().getResource("/snd/Notification.mp3").toExternalForm());
        mediaPlayer = new MediaPlayer(sound);
        //Play animation and Sound during application start
        Platform.runLater(() -> {
            playAnimation();
        });
    }

    public static void playAnimation(){
        Platform.runLater(() -> {
            mediaPlayer.play();
            primaryStage.show();
        });
        // Schedule closing the stage after 5 seconds
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.schedule(() -> {
            Platform.runLater(() -> {
                mediaPlayer.stop();
                primaryStage.close();
            });
        }, 5, TimeUnit.SECONDS);
    }
    public static void runFromScheduledExecutor() {
        Application.launch(NotificationApp.class);
    }
}

问题原因与修复方案

核心问题

  1. MediaPlayer状态未重置:首次播放后,MediaPlayer的播放位置停在音频末尾,后续调用play()不会从头开始播放。
  2. Stage重复关闭/显示的状态冲突:Stage关闭后再次调用show()可能引发状态异常,导致动画无法正常显示。
  3. 定时线程池资源泄漏:每次调用playAnimation()都创建新的ScheduledExecutorService,未回收线程资源。

修复步骤

  1. 重置MediaPlayer播放位置
    每次播放前将音频进度重置到起始位置,确保从头播放:

    public static void playAnimation(){
        Platform.runLater(() -> {
            mediaPlayer.seek(Duration.ZERO); // 重置到音频开头
            mediaPlayer.play();
            primaryStage.show();
        });
        // ... 其他代码
    }
    
  2. 改用Stage.hide()替代close()
    避免反复关闭Stage,改为隐藏,减少状态异常:

    scheduler.schedule(() -> {
        Platform.runLater(() -> {
            mediaPlayer.stop();
            primaryStage.hide(); // 隐藏而非关闭
        });
    }, 5, TimeUnit.SECONDS);
    
  3. 复用全局定时线程池
    全局维护一个线程池,避免重复创建导致的资源泄漏:

    // 在NotificationApp中添加全局线程池
    private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    
    public static void playAnimation(){
        Platform.runLater(() -> {
            mediaPlayer.seek(Duration.ZERO);
            mediaPlayer.play();
            primaryStage.show();
        });
        // 使用全局线程池
        scheduler.schedule(() -> {
            Platform.runLater(() -> {
                mediaPlayer.stop();
                primaryStage.hide();
            });
        }, 5, TimeUnit.SECONDS);
    }
    
    // 程序退出时关闭线程池
    @Override
    public void stop() throws Exception {
        scheduler.shutdown();
        super.stop();
    }
    
  4. 统一资源加载方式
    图片路径改用和音频一致的类加载器方式,避免文件路径问题:

    Image gifImage = new Image(NotificationApp.class.getResource("/img/giphy.gif").toExternalForm());
    

内容的提问来源于stack exchange,提问作者Lalit k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 20:59:55