Performance JavaFX2-将定制(fxml)面板动态添加到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

问题 我想在运行时将通过javafx场景生成器构建的定制面板添加到gridpane中。我的定制面板包括按钮、标签等

我的尝试 我试图从窗格扩展

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;
        }
    }