Dim*_*ris 8 java netbeans javafx javafx-2
我正在构建一个JavaFX应用程序,我想知道是否有关于如何尽可能快地加载新Scene
电流的建议(最佳实践)Stage
.
目前我正在做的是(或多或少)这个:
Parent root = (Parent)myFXLoader.load();
currentStage.setScene(new Scene (root);
Run Code Online (Sandbox Code Playgroud)
上面的工作很好,速度足够简单Scene
s但是当加载更复杂的场景初始化TableView
s,Combobox
es等时,Scene
s 之间的过渡需要很多秒,这很烦人.
在我Controller
的initialize(URL url, ResourceBundle rb)
方法中进行的所有初始化.
在那里我将项目添加到Choice/Combo
框中,初始化TableView
等,但正如我所说,它需要太多时间.
难道我做错了什么?我应该在其他地方初始化吗?
谢谢.
编辑:
任何有兴趣帮助这个,甚至有兴趣为他们的项目,我已经上传了我的项目(Netbeans项目)的一部分在google.com.
你可以使用SVN查看它.这是链接:
http
://tabularasafx.googlecode.com/svn/trunk/ userName:tabularasafx-read-only
no password required
运行项目后的说明:
第一个屏幕是登录界面,只需单击OK
第二个屏幕是"homePage ",你可以看到一个treeView菜单并导航到4个不同的屏幕
我的问题是类的加载时间 - >创建页面.看看它,如果你发现任何
编辑,请告诉我:
我对@jewelsea建议进行了3次更改.
1.我使用HashMap来保存每个屏幕的所有控制器
2.我只更新场景的一部分而不是整个场景
3.我使用了JavaFX2的答案- 在向gridpane添加自定义制作(fxml)面板时性能非常差动态地帮助控制器加载更快,如答案中所述.
现在一切都快得多!!!!
随意使用该项目作为指导
我也更新程序以浏览3个屏幕,以便更好地理解
我的代码是凌乱的
jew*_*sea 20
一些背景
我看了看你的Dimitris项目.
我为"类创建"页面计算了您的负载创建时间(OS X 10.9,2012 Macbook Air上的Java 8 b129).我花了一秒钟.
为了简化测试,我删除了使用并发服务加载新FXML的部分,并在请求时将FXML直接加载到JavaFX应用程序线程上 - 使用这种方式更容易.
对不起,这里的答案很长.这样的事情通常不适合StackOverflow,它们在教程或博客形式中最好,但我很好奇发生了什么,所以我想我需要一些时间来研究它并写下来起来.
不要为您加载的每个FXML创建一个新场景
每次加载FXML时都设置一个新场景(使用新的大小).无论出于何种原因,这是一项非常昂贵的操作,您无需这样做.您已经在舞台上有一个场景,只需重复使用它.所以替换下面的代码:
stage.setScene(new Scene(service.getValue().getRoot(), service.getValue().getX(), service.getValue().getY()));
Run Code Online (Sandbox Code Playgroud)
有:
stage.getScene().setRoot(service.getValue().getRoot());
Run Code Online (Sandbox Code Playgroud)
这将在加载时间上节省超过半秒,因此现在class-> create在第一次运行时大约需要400毫秒.
此更改是轻松获得性能的一个示例.
它还提供了更好的用户体验,因为在我的机器上,当您更换场景时,舞台闪烁灰色,但是当您只是替换现有场景的场景根时,没有灰色闪光.
由于JVM使用Java的即时编译器运行,因此后续显示classes-> create的请求更快,因此在打开场景两到三次后,它需要大约250ms(或四分之一秒).
FXMLLoader很慢
在剩余的250ms加载中,大约2ms用于初始化代码,JavaFX渲染控件花费另外2ms,FXMLLoader花费其他246ms加载FXML并实例化节点进入场景.
使用UI代码的想法是,您希望将转换的目标时间缩短到<16到30毫秒.这将使用户快速顺利地过渡.
将UI代码与网络和数据库代码分开
网络和数据库调用最好是从JavaFX应用程序线程完成的,因此您可以使用JavaFX并发工具来包装这些任务.但我建议分开关注点.使用并发服务来获取数据,但是一旦获得数据,使用Platform.runLater或Task返回值来传输JavaFX应用程序线程的数据并在JavaFX应用程序线程上运行填充(因为该填充任务将是反正很快).
这样,您就可以将系统中的多线程划分为不同的逻辑组件 - 网络在其自己的线程上运行,UI操作在不同的线程上运行.它使事情更容易推理和设计.可以认为它有点像Web编程,其中ajax调用同时向UI提取数据,然后提供调用以将数据处理到UI中的回调.
这样做的另一个原因是许多网络库无论如何都带有自己的线程实现,因此您只需使用它而不是生成自己的线程.
如何使FXML加载更快
您不应该真正需要多线程代码来加载FXML文件.FXML的初始化功能运行得非常快(仅几毫秒).FXMLLoader需要250ms.我没有详细介绍它,看看为什么会这样.但是Sebastian对JavaFX2的回答有一些迹象 - 在动态地将定制(fxml)面板添加到gridpane时性能非常差.我认为主要的性能问题是FXMLLoader非常依赖于反射.
因此,在FXMLLoader缓慢出现问题的情况下,最佳解决方案是使用FXMLLoader的替代方案,该方案性能更好,不依赖于反射.我相信JavaFX团队正在研究FXMLLoader的二进制等价物(例如,FXML文件在构建阶段被预先解析为二进制Java类文件,可以快速加载到JVM中).但JavaFX团队尚未发布该工作(如果存在).Tom Schindl已经完成了类似的工作,它将FXML预编译为Java源代码,然后可以将其编译为Java类,因此您的应用程序再次使用编译的类,这应该是很好的和快速的.
因此,使FXML加载速度更快的解决方案目前正在开发中,但在生产系统上并不是非常稳定和可用.所以你需要其他方法来处理这个问题.
使您的表单更简单
这对我来说似乎是一种愚蠢的行为,但IMO对你的"创建类"场景的设计有点复杂.您可能需要考虑使用多阶段向导替换它.这样的向导通常会加载更快,因为您只需要在每个向导屏幕上加载一些项目.但更重要的一点是,这样的向导可能更容易使用,并为您的用户提供更好的设计.
仅替换场景中需要的部分
您正在加载FXML文件,这些文件为每个新页面创建整个应用程序UI.但是您不需要这样做,因为顶级菜单,状态栏和导航侧栏等内容不会因为用户加载新表单而改变 - 只显示"创建类"表单的中央部分正在发生变化.因此,只需加载正在更改的场景部分的节点,而不是整个场景内容.
此外,这将通过在每个阶段替换整个UI来帮助解决您的应用程序将遇到的其他问题.当您更换导航菜单时,菜单不会自动记住并突出显示导航树中当前选定的项目 - 您必须明确记住它并在执行导航后再次重置它.但是如果你没有更换整个场景内容,导航菜单会记住上次选择的内容并显示它(因为导航菜单本身在导航时没有改变).
缓存FXML加载节点树和控制器
您只需在应用程序中一次显示一个"创建类"表单.所以你只需要使用FXMLLoader来加载"创建类"表单一次.这将为表单创建一个节点树.定义一个静态HashMap,它将"create classes"映射到CreateClassesController对象(在应用程序中也只有一个).当您导航到"创建类"屏幕时,通过从哈希映射中检索控制器,查看您之前是否已经在那里.如果已存在现有控制器类,则查询它以获取表单的根窗格,并通过用新表单替换场景的中心面板在场景中显示表单.您可以在控制器上添加额外的方法,您可以调用这些方法来清除表单中的任何现有数据值,或者设置从网络提取任务加载的任何数据值.
除了加速应用程序之外,您现在还可以保留"创建类"表单的状态,直到您或用户决定清除它为止.这意味着用户可以通过并部分填写表单到应用程序中的其他位置然后返回到表单,它将处于与它们离开时相同的状态,而不是忘记用户之前输入的所有内容.
现在因为您只加载了"创建类"表单一次,您可以在启动时加载所有表单(并有一个预加载器页面,指示您的应用程序正在初始化).这意味着应用程序的初始启动速度会较慢,但应用程序的运行速度会很快.
建议的设计
查看SceneBuilder实现
遵循SceneBuilder实现本身使用的原则- 它是适合大小的JavaFX项目的当前最佳设计示例,该项目使用FXML作为其UI.SceneBuilder代码是开源的,并以BSD风格的许可证分发,因此很适合学习.
结果
我对这个答案中提到的一些想法进行了原型设计,这将"创建类"屏幕的初始加载时间从一秒钟缩短到大约400毫秒(第一次加载屏幕).我没有用其他东西替换FXMLLoader(我肯定会大大减少400ms的值).基于刚刚重新添加到场景的缓存节点树的"创建类"表单的后续加载大约需要4ms - 因此,就用户而言,操作性能是即时的.
其他问题的更新
您是否认为我应该使用Tom Schindl的编译FXML的解决方案,还是"太Beta"?
我的猜测是(截至今天)它是"太Beta".但请亲自尝试一下,看看它是否符合您的需求.有关Tom的FXML => JavaFX编译器的支持,请发布到e(fx)clipse论坛,因为该项目属于e(fx)clipse项目的更大范围.
我试过'stage.getScene().setRoot(service.getValue().getRoot());' 但得到了OutOfMemoryError:Java堆空间你认为该行导致它还是不相关?
作为创建此答案的一部分,我正在对您的代码进行一些分析(通过将NetBeans探查器附加到已运行的应用程序实例).我注意到每次你的程序加载"创建类"场景时,内存使用量会显着增长并且内存似乎没有被释放.我没有花时间试图找出原因是什么,但那是未修改的代码分析.所以我怀疑系统耗尽内存的最终原因与你是换掉一个场景还是换掉一个场景根无关.我注意到CSS psuedo-classes消耗了大量内存,但我无法告诉你原因.我的猜测是,如果您遵循本答案中概述的原则,那么总的来说,您的应用程序将更加高效,并且您可以规避当前代码中存在的与内存相关的问题.如果没有,您可以继续分析应用程序内存使用情况,以查看根本问题.
归档时间: |
|
查看次数: |
8918 次 |
最近记录: |