JavaFx和内存消耗

时间:2014-04-10 14:01:04

标签: java memory javafx

我刚刚使用java fx制作了一个应用程序来循环文件夹中的视频。 我不得不设置下一个视频触发setonendofmedia事件,因此它们处于一个循环中,但问题是应用程序在开始时加载每个视频,所以一段时间后它会填充内存和崩溃。 有没有其他方法可以在不预加载视频的情况下循环播放视频或者每次都刷新内存的方法?

这是我的代码:

 package application;


    import java.io.File;
    import java.io.FilenameFilter;
    import java.util.ArrayList;
    import java.util.List;

    import javax.swing.JOptionPane;

    import javafx.application.Platform;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBoxBuilder;
    import javafx.scene.media.Media;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaView;

    class SceneGenerator {    

        private int xSize;
        private int ySize;

        public SceneGenerator(int xSize,int ySize){
            this.xSize=xSize;
            this.ySize=ySize;
        }

      public Scene createScene() {
        final StackPane layout = new StackPane();

        // determine the source directory for the playlist
        final File dir = new File(System.getProperty("user.dir")+"\\video");
        if (!dir.exists() || !dir.isDirectory()) {
          JOptionPane.showMessageDialog(null,"Cannot find video source directory: " + dir);
          Platform.exit();
          System.exit(0);
          return null;
        }

        //inizializzo il toolkit


        // create some media players.
        final List<MediaPlayer> players = new ArrayList<MediaPlayer>();
        for (String file : dir.list(new FilenameFilter() {
          @Override public boolean accept(File dir, String name) {
            return name.endsWith(".mp4")||name.endsWith(".flv");
          }
        })) players.add(createPlayer("file:///" + (dir + "\\" + file).replace("\\", "/").replaceAll(" ", "%20")));
        if (players.isEmpty()) {
          System.out.println("No video found in " + dir);
          Platform.exit();
          System.exit(0);
          return null;
        }    

        // create a view to show the mediaplayers.
        final MediaView mediaView = new MediaView(players.get(0));

            mediaView.setPreserveRatio(false);
            mediaView.setFitHeight(ySize-((ySize/100)*(JavaPlayer.panelSouthYDimension+1)-JavaPlayer.pixelAdattamento));
            mediaView.setFitWidth(xSize);


        // play each audio file in turn.
        for (int i = 0; i < players.size(); i++) {
          final MediaPlayer player     = players.get(i);
          final MediaPlayer nextPlayer = players.get((i + 1) % players.size());



          player.setOnEndOfMedia(new Runnable() {
            @Override public void run() {
              mediaView.setMediaPlayer(nextPlayer);
              nextPlayer.seek(nextPlayer.getStartTime());
              nextPlayer.play();
            }
          });
        }






        // start playing the first track.
        mediaView.setMediaPlayer(players.get(0));
        mediaView.getMediaPlayer().play();



        // layout the scene.
        layout.setStyle("-fx-background-color: black; -fx-font-size: 20; -fx-padding: 0; -fx-alignment: center;");
        layout.getChildren().addAll(
          VBoxBuilder.create().spacing(10).alignment(Pos.CENTER).children(
            mediaView).build()
        );

        return new Scene(layout);
      }

      /** sets the currently playing label to the label of the new media player and updates the progress monitor. */


      /** @return a MediaPlayer for the given source which will report any errors it encounters */
      private MediaPlayer createPlayer(String aMediaSrc) {
    //    System.out.println("Creating player for: " + aMediaSrc);
        final MediaPlayer player = new MediaPlayer(new Media(aMediaSrc));
        player.setOnError(new Runnable() {
          @Override public void run() {
            System.out.println("Media error occurred: " + player.getError());
          }
        });
        return player;
      }
    }

非常感谢您的代码,我目前正在使用java 7并且一切正常,但是一旦加载了视频,它就永远不会被释放,因此内存消耗仍然很高。 我尝试使用system.gc,但似乎还不够。一些建议?提前谢谢。

class SceneGenerator {    

private int xSize;
private int ySize;

public SceneGenerator(int xSize,int ySize){
    this.xSize=xSize;
    this.ySize=ySize;
}

public Sc​​ene createScene(){     final StackPane layout = new StackPane();

// determine the source directory for the playlist
final File dir = new File(System.getProperty("user.dir")+"\\video");
if (!dir.exists() || !dir.isDirectory()) {
  JOptionPane.showMessageDialog(null,"Cannot find video source directory: " + dir);
  Platform.exit();
  System.exit(0);
  return null;
}

//inizializzo il toolkit


final MediaView mediaView = new MediaView();

    mediaView.setPreserveRatio(false);
    mediaView.setFitHeight(ySize-((ySize/100)*(JavaPlayer.panelSouthYDimension+1)-JavaPlayer.pixelAdattamento));
    mediaView.setFitWidth(xSize);


    final int QUEUE_SIZE = 2 ; // should be enough
    final BlockingQueue<MediaPlayer> playerQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);


    final Thread createPlayerThread = new Thread(new Runnable() {
        @Override
        public void run() {
             // create some media players.
            List<String> videoFiles = new ArrayList<String>();
            for (String file : dir.list(new FilenameFilter() {
              @Override public boolean accept(File dir, String name) {
                return name.endsWith(".mp4")||name.endsWith(".flv");
              }
            })) videoFiles.add("file:///" + (dir + "\\" + file).replace("\\", "/").replaceAll(" ", "%20"));
            int nextFileIndex = 0 ;

            while (true) {
                System.gc();
                MediaPlayer player = new MediaPlayer(new Media(videoFiles.get(nextFileIndex).toString())); // create
                player.setOnEndOfMedia(new Runnable() {
                    @Override
                    public void run() {
                        final Task<MediaPlayer> nextPlayerTask = new Task<MediaPlayer>() {
                            @Override
                            public MediaPlayer call()  {
                                try {
                                    return playerQueue.take();
                                } catch (InterruptedException e) {
                                    JOptionPane.showMessageDialog(null, "Error in SceneGenerator() : "+e.getMessage());
                                    return null;
                                }
                            }
                        };
                        nextPlayerTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
                            @Override
                            public void handle(WorkerStateEvent event) {
                                MediaPlayer player = nextPlayerTask.getValue();
                                mediaView.setMediaPlayer(player);
                                player.play();
                            }
                        });
                        new Thread(nextPlayerTask).start();
                    }
                });
                try {
                    playerQueue.put(player); // this will block if the queue is full...
                } catch (InterruptedException exc ) { // shouldn't happen...
                    exc.printStackTrace();
                    return ;
                }
                nextFileIndex = (nextFileIndex + 1) % videoFiles.size();
            }
        }
    });
    createPlayerThread.setDaemon(true); // won't block application exit

    // Layout etc

    createPlayerThread.start();
    // start first player. In theory, this could block, so call it before you show the stage:
    MediaPlayer firstPlayer;
    try {
        firstPlayer = playerQueue.take();
        mediaView.setMediaPlayer(firstPlayer);
        firstPlayer.play();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        JOptionPane.showMessageDialog(null, "Error in SceneGenerator() : "+e.getMessage());
    }



// layout the scene.
layout.setStyle("-fx-background-color: black; -fx-font-size: 20; -fx-padding: 0; -fx-alignment: center;");
layout.getChildren().addAll(
  VBoxBuilder.create().spacing(10).alignment(Pos.CENTER).children(
    mediaView).build()
);

return new Scene(layout);

}

}

1 个答案:

答案 0 :(得分:2)

我会通过创建BlockingQueue<MediaPlayer>来存储有限数量的媒体播放器。创建一个循环文件​​的后台线程,从每个文件创建一个媒体播放器,并将其放入队列中。媒体播放器应该在其中注册onEndOfMedia侦听器。听众会或多或少地拥有它,但你从队列中取出下一个MediaPlayer(这也应该在后台线程中)并播放它。它变得稍微复杂一些,因为你必须管理线程,因为你有阻塞调用,但这样的事情(这只是近似给你的想法;我还没有测试它或尝试编译或任何东西):

    final int QUEUE_SIZE = 2;
    final BlockingQueue<MediaPlayer> playerQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
    final MediaView mediaView = new MediaView();


    Thread createPlayerThread = new Thread( () -> {
        final List<File> videoFiles = ...
        IntStream.iterate(0, index -> (index + 1) % videoFiles.size())
            .mapToObj(videoFiles::get)
            .map(file -> createPlayer(file, mediaView, playerQueue))
            .forEach(player -> {
                try {
                    playerQueue.put(player);
                } catch (Exception e) {
                    e.printStackTrace();
                    return ;
                }
            });
    });
    createPlayerThread.setDaemon(true);
    createPlayerThread.start();

    // do layout, etc...

    try {
        mediaView.setMediaPlayer(playerQueue.take());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // ...

private MediaPlayer createPlayer(File file, MediaView mediaView, BlockingQueue<MediaPlayer> playerQueue) {
    URI uri = file.toURI();
    Media media = new Media(uri.toString());
    MediaPlayer player = new MediaPlayer(media);
    player.setAutoPlay(true);
    player.setOnEndOfMedia( () -> {
        Task<MediaPlayer> nextPlayerTask = new Task<MediaPlayer>() {
            @Override
            protected MediaPlayer call() throws Exception {
                return playerQueue.take();
            }
        };
        nextPlayerTask.setOnSucceeded(workerStateEvent -> 
            mediaView.setMediaPlayer(nextPlayerTask.getValue()));
    });
    return player ;
}

如果队列大小太大,则存在内存不足的风险。如果它太小,那么从理论上讲,你可能不会在队列中有任何可用的视频(如果它们需要更长的时间来加载而不是播放);在实践中,这可能不太可能。如果队列大小太小,代码仍然有效,但视频之间会有暂停。

相关问题