Performance JavaFX2-将定制(fxml)面板动态添加到gridpane时性能非常差
问题 我想在运行时将通过javafx场景生成器构建的定制面板添加到gridpane中。我的定制面板包括按钮、标签等 我的尝试 我试图从窗格扩展Performance JavaFX2-将定制(fxml)面板动态添加到gridpane时性能非常差,performance,javafx-2,fxml,scenebuilder,Performance,Javafx 2,Fxml,Scenebuilder,问题 我想在运行时将通过javafx场景生成器构建的定制面板添加到gridpane中。我的定制面板包括按钮、标签等 我的尝试 我试图从窗格扩展 public class Celli extends Pane{ public Celli() throws IOException{ Parent root = FXMLLoader.load(getClass().getResource("Cell.fxml")); this.getChildren().add
public class Celli extends Pane{
public Celli() throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("Cell.fxml"));
this.getChildren().add(root);
}
}
。。。然后在conroller的添加方法中使用此面板
@FXML
private void textChange(KeyEvent event) {
GridPane g = new GridPane();
for (int i=0 : i<100; i++){
g.getChildren().add(new Celli());
}
}
}
@FXML
私有void textChange(KeyEvent事件){
GridPane g=新的GridPane();
对于(inti=0:i简短回答:不,它不是(从JavaFX2.x和8.0开始)。它可能在未来的版本中(JFX>8)
长答案:
FXMLLoader目前不是作为一个模板提供程序来执行的,它反复实例化同一个项目,而是作为大型GUI的一次性加载程序(或序列化它们)
性能很差,因为根据FXML文件的不同,每次调用load()
,FXMLLoader都必须通过反射查找类及其属性。这意味着:
对于每个导入语句,尝试加载每个类,直到成功加载该类
对于每个类,创建一个BeanAdapter,用于查找该类具有的所有属性,并尝试将给定参数应用于该属性
参数对属性的应用再次通过反射完成
对代码中完成的相同FXML文件的后续调用load()
目前也没有改进。这意味着:没有找到的类的缓存,没有BeanAdapter的缓存等等
不过,通过将自定义类加载器设置为FXMLLoader实例,可以解决步骤1的性能问题:
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class MyClassLoader extends ClassLoader{
private final Map<String, Class> classes = new HashMap<String, Class>();
private final ClassLoader parent;
public MyClassLoader(ClassLoader parent) {
this.parent = parent;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = findClass(name);
if ( c == null ) {
throw new ClassNotFoundException( name );
}
return c;
}
@Override
protected Class<?> findClass( String className ) throws ClassNotFoundException {
// System.out.print("try to load " + className);
if (classes.containsKey(className)) {
Class<?> result = classes.get(className);
return result;
} else {
try {
Class<?> result = parent.loadClass(className);
// System.out.println(" -> success!");
classes.put(className, result);
return result;
} catch (ClassNotFoundException ignore) {
// System.out.println();
classes.put(className, null);
return null;
}
}
}
// ========= delegating methods =============
@Override
public URL getResource( String name ) {
return parent.getResource(name);
}
@Override
public Enumeration<URL> getResources( String name ) throws IOException {
return parent.getResources(name);
}
@Override
public String toString() {
return parent.toString();
}
@Override
public void setDefaultAssertionStatus(boolean enabled) {
parent.setDefaultAssertionStatus(enabled);
}
@Override
public void setPackageAssertionStatus(String packageName, boolean enabled) {
parent.setPackageAssertionStatus(packageName, enabled);
}
@Override
public void setClassAssertionStatus(String className, boolean enabled) {
parent.setClassAssertionStatus(className, enabled);
}
@Override
public void clearAssertionStatus() {
parent.clearAssertionStatus();
}
}
这大大加快了性能。但是,步骤2没有解决方法,因此这可能仍然是一个问题
然而,官方JavaFX jira中已经有了这方面的功能请求。如果您能支持这一请求,那将是非常好的
链接:
- FXMLLoader应该能够在to load()调用之间缓存导入和属性:
- 将setAdapterFactory()添加到FXMLLoader:
我也遇到了类似的问题。我还必须多次动态加载基于fxml的自定义组件,而且时间太长。在我的情况下,FXMLLoader.load方法调用非常昂贵
我的方法是并行化组件实例化,解决了这个问题
考虑到问题上的示例,采用多线程方法的控制器方法为:
private void textChange(KeyEvent event) {
GridPane g = new GridPane();
// creates a thread pool with 10 threads
ExecutorService threadPool = Executors.newFixedThreadPool(10);
final List<Celli> listOfComponents = Collections.synchronizedList(new ArrayList<Celli>(100));
for (int i = 0; i < 100; i++) {
// parallelizes component loading
threadPool.execute(new Runnable() {
@Override
public void run() {
listOfComponents.add(new Celli());
}
});
}
// waits until all threads completion
try {
threadPool.shutdown();
threadPool.awaitTermination(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// seems to be a improbable exception, but we have to deal with it
e.printStackTrace();
}
g.getChildren().addAll(listOfComponents);
}
private void textChange(KeyEvent事件){
GridPane g=新的GridPane();
//创建一个包含10个线程的线程池
ExecutorService线程池=Executors.newFixedThreadPool(10);
组件的最终列表=Collections.synchronizedList(新的ArrayList(100));
对于(int i=0;i<100;i++){
//并行化组件加载
执行(新的Runnable(){
@凌驾
公开募捐{
添加(新Celli());
}
});
}
//等待所有线程完成
试一试{
threadPool.shutdown();
线程池。等待终止(3,时间单位。秒);
}捕捉(中断异常e){
//这似乎是一个不太可能的例外,但我们必须处理它
e、 printStackTrace();
}
g、 getChildren().addAll(组件列表);
}
只需在@Sebastian sir给定的代码中添加“缓存已加载的类”的代码。这对我有效。请建议对其进行更改以获得更好的性能
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("In Class loader");
Class result;
System.out.println(" >>>>>> Load class : "+name);
result = (Class)classes.get(name);
if(result != null){
System.out.println(" >>>>>> returning cached class.");
return result;
}else{
Class<?> c = findClass(name);
if ( c == null ) {
throw new ClassNotFoundException( name );
}
System.out.println(" >>>>>> loading new class for first time only");
return c;
}
}
@覆盖
公共类loadClass(字符串名称)引发ClassNotFoundException{
System.out.println(“类内加载器”);
班级成绩;
System.out.println(“>>>>>加载类:“+name”);
result=(Class)classes.get(name);
如果(结果!=null){
System.out.println(“>>>>返回缓存类”);
返回结果;
}否则{
c类=findClass(名称);
如果(c==null){
抛出新的ClassNotFoundException(名称);
}
System.out.println(“>>>>>仅首次加载新类”);
返回c;
}
}
您使用的是哪个版本的JavaFx 2.2或8.O?对于JavaFX8.0,您的方法失败,出现异常:“java.lang.IllegalStateException:不在FX应用程序线程上…”最后的链接不再有效。有人知道他们过去指向的是什么,并且可以找到与之对应的更新的JDK-???ID吗?@Itai,我已经更新了死链接。您可以在新的错误跟踪器上搜索错误ID。请参阅FibreFoX对“如何在新的JavaFx错误跟踪器中查找错误”的回答:我能够通过使用一个更快的纯java XML解析器来实现一些性能提升,但这种改变确实深入地解决了一个基本的体系结构问题:我错误地使用了FXML。我不建议使用FXML加载程序作为模板。
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("In Class loader");
Class result;
System.out.println(" >>>>>> Load class : "+name);
result = (Class)classes.get(name);
if(result != null){
System.out.println(" >>>>>> returning cached class.");
return result;
}else{
Class<?> c = findClass(name);
if ( c == null ) {
throw new ClassNotFoundException( name );
}
System.out.println(" >>>>>> loading new class for first time only");
return c;
}
}