简短描述:运行一个简单的 JavaFX 程序来说明 AnimationTimer 导致我的 PC 几乎锁定。当使用其他版本的 Linux 启动时,该程序在其他 PC 和我的 PC 上运行良好。详细说明如下。
在校对新版本的在线 Java 教科书时,我在运行书中的示例程序时遇到了问题,该示例程序说明了 JavaFX AnimationTimer。程序源是 SimpleAnimationTimer.java。见下文。这是一个非常简单的程序,它只显示当前帧数和已用时间(以秒为单位)。预期的输出是看到“每秒 60 帧”;也就是说,显示的帧数除以显示的经过时间应该是60。
在我的 PC 上编译并运行时,它淹没了应用程序窗口,以至于我几乎无法将鼠标移动到另一个窗口,这样我就可以杀死正在运行的程序。当我通知这本书的作者时,他回答说该程序在他的所有 PC(Ubuntu、Linux Mint、Mac、Windows)上都运行良好。我的电脑运行的是 Fedora 27。
我从实时 USB 下载并运行最新的 Linux Mint,然后使用与 Fedora 27 相同的 Java 测试该程序。该程序在那里运行没有错误。
接下来,我重新启动了较旧的 Fedora 24 并在那里测试了该程序。它运行没有错误。
我从实时 USB 下载并运行 Fedora 28。程序有问题。
我的测试环境和结果的总结。
Java 版本是 1.8.0_181。它是来自 Oracle 的一个。
Linux Mint 19 内核 4.15.0-20-generic。没问题。
Fedora 24 内核 4.11.12。没问题。
Fedora 27 内核 4.17.17。有问题。
Fedora 28 内核 4.16.3。有问题。
内核版本 4.15 和 4.16 之间的 Linux 中的某些更改似乎导致了问题。
原始 SimpleAnimationStarter.java 源代码被修改为在 60 帧后停止。Test02SimpleAnimationStarter.java 源代码(如下所示)就是这样做的。当这个程序运行时,显示的帧数是 60,正如预期的那样,显示的经过时间是 0.3 秒,而不是预期的 1 秒。
目前我不知道该怎么办。这是 JavaFX 问题吗?这是Linux内核问题吗?还有什么?
任何建议/意见将不胜感激。此外,在其他平台(Windows、Mac 和其他 Linux 版本)上运行这两个程序时,很高兴看到结果。
警告:首先运行 Test02 版本!确保经过的时间是一秒。
来源如下。首先是 SimpleAnimationStarter.java,然后是 Test02SimpleAnimationStarter.java。
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.layout.BorderPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
/**
 *  This file can be used to create very simple animations.  Just fill in
 *  the definition of drawFrame with the code to draw one frame of the 
 *  animation, and possibly change a few of the values in the rest of
 *  the program as noted below.
 */
public class SimpleAnimationStarter extends Application {
    /**
     * Draws one frame of an animation. This subroutine should be called
     * about 60 times per second.  It is responsible for redrawing the
     * entire drawing area. The parameter g is used for drawing. The frameNumber 
     * starts at zero and increases by 1 each time this subroutine is called.  
     * The parameter elapsedSeconds gives the number of seconds since the animation
     * was started.  By using frameNumber and/or elapsedSeconds in the drawing
     * code, you can make a picture that changes over time.  That's an animation.
     * The parameters width and height give the size of the drawing area, in pixels.  
     */
    public void drawFrame(GraphicsContext g, int frameNumber, double elapsedSeconds, int width, int height) {
        /* NOTE:  To get a different animation, just erase the contents of this 
         * subroutine and substitute your own. 
         */
        g.setFill(Color.WHITE);
        g.fillRect(0, 0, width, height); // First, fill the entire image with a background color!
        g.setFill(Color.BLACK);
        g.fillText( "Frame number " + frameNumber, 40, 50 );
        g.fillText( String.format("Elapsed Time: %1.1f seconds", elapsedSeconds), 40, 80);
    }
    //------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------
    private int frameNum;
    private long startTime;
    public void start(Stage stage) {
        int width = 800;   // The width of the image.  You can modify this value!
        int height = 600;  // The height of the image. You can modify this value!
        Canvas canvas = new Canvas(width,height);
        drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
        BorderPane root = new BorderPane(canvas);
        root.setStyle("-fx-border-width: 4px; -fx-border-color: #444");
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Simple Animation"); // STRING APPEARS IN WINDOW TITLEBAR!
        stage.show();
        stage.setResizable(false);
        AnimationTimer anim = new AnimationTimer() {
            public void handle(long now) {
                if (startTime < 0)
                    startTime = now;
                frameNum++;
                drawFrame(canvas.getGraphicsContext2D(), frameNum, (now-startTime)/1e9, width, height);
            }
        };
        startTime = -1;
        anim.start();
    } 
    public static void main(String[] args) {
        launch();
    }
} // end SimpleAnimationStarter
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.layout.BorderPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
/**
 *  This file can be used to create very simple animations.  Just fill in
 *  the definition of drawFrame with the code to draw one frame of the 
 *  animation, and possibly change a few of the values in the rest of
 *  the program as noted below.
 */
public class Test02SimpleAnimationStarter extends Application {
    /**
     * Draws one frame of an animation. This subroutine should be called
     * about 60 times per second.  It is responsible for redrawing the
     * entire drawing area. The parameter g is used for drawing. The frameNumber 
     * starts at zero and increases by 1 each time this subroutine is called.  
     * The parameter elapsedSeconds gives the number of seconds since the animation
     * was started.  By using frameNumber and/or elapsedSeconds in the drawing
     * code, you can make a picture that changes over time.  That's an animation.
     * The parameters width and height give the size of the drawing area, in pixels.  
     */
    public void drawFrame(GraphicsContext g, int frameNumber, double elapsedSeconds, int width, int height) {
        /* NOTE:  To get a different animation, just erase the contents of this 
         * subroutine and substitute your own. 
         */
        g.setFill(Color.WHITE);
        g.fillRect(0, 0, width, height); // First, fill the entire image with a background color!
        g.setFill(Color.BLACK);
        g.fillText( "Frame number " + frameNumber, 40, 50 );
        g.fillText( String.format("Elapsed Time: %1.1f seconds", elapsedSeconds), 40, 80);
    }
    //------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------
    private int frameNum;
    private long startTime;
    AnimationTimer anim = null;  // Moved outside start() so handle() can call anim.stop()
    public void start(Stage stage) {
        int width = 800;   // The width of the image.  You can modify this value!
        int height = 600;  // The height of the image. You can modify this value!
        Canvas canvas = new Canvas(width,height);
        drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
        BorderPane root = new BorderPane(canvas);
        root.setStyle("-fx-border-width: 4px; -fx-border-color: #444");
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Simple Animation"); // STRING APPEARS IN WINDOW TITLEBAR!
        stage.show();
        stage.setResizable(false);
//        AnimationTimer anim = new AnimationTimer() {            
        anim = new AnimationTimer() {
            // Constants: change NUM_SECS and/or framesPerSec
            private final int NUM_SECS = 1; // Desired number of seconds.
            private final int framesPerSec = 60; // Expected frames per second.
            private final int NUM_FRAMES = NUM_SECS*framesPerSec;
            public void handle(long now) {
                if (startTime < 0)
                    startTime = now;
                frameNum++;
                if (frameNum < NUM_FRAMES+1)
                {
                    drawFrame(canvas.getGraphicsContext2D(),
                              frameNum,
                              (now-startTime)/1e9,
                              width, height);
                }
                else if (frameNum == NUM_FRAMES+1)
                {
                    anim.stop();
                    System.out.println("Animation stopped");
                }
                else if (frameNum == NUM_FRAMES+2)
                {
                    System.out.println("Animation kept going after stop ???");
                }
            }
        };
        startTime = -1;
        anim.start();
    }
    public static void main(String[] args) {
        launch();
    }
} // end Test02SimpleAnimationStarter
| 归档时间: | 
 | 
| 查看次数: | 876 次 | 
| 最近记录: |