5

i made a class as a loader for loading images in background Thread , to add them inside the JavaFX Application ,

my problem is , although of this class works as a Task , but it cause the JavaFX Apllication freeze during images loading , and work back again normally after it finish,

The Loader Class Code :

import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

public class Loader{
    private String type     = "";
    private String url      = "";
    private ImageView image = new ImageView();

    private class LoaderService extends Service{
        @Override
        protected Task createTask() {
            return new LoaderTask();
        }
    }

    private class LoaderTask extends Task{
        @Override
        protected Object call() throws Exception {
            new LoaderClass();

            return null;
        }
    }

    private String getType(){
        return this.type;
    }

    private String getUrl(){
        return this.url;
    }

    public Loader type(String type){
        this.type = type;
        return this;
    }

    public Loader url(String url){
        this.url = url;
        return this;
    }

    public ImageView getImage(){
        return this.image;
    }

    public Loader build(){
        new LoaderTask().run();
        return this;
    }

    private class LoaderClass{
        public LoaderClass(){
            switch(getType()){
                case "image":
                    this.loadImage(getUrl());
                break;
            }
        }

        private void loadImage(String url){
            try{
                getImage().setImage(new Image(url));
            }catch(Exception ex){
                System.out.println("Ex"+ex.getMessage());
            }
        }
    }
}

Example of calling the loader for images from external class to add them inside the main JavaFX Window :

StackPane Pane = new StackPane();

ImageView Icon1 = new NinjaLoader()
                                    .type("image")
                                    .url("http://localhost/images/1.png")
                                    .build()
                                    .getImage();

ImageView Icon2 = new NinjaLoader()
                                    .type("image")
                                    .url("http://localhost/images/2.png")
                                    .build()
                                    .getImage();

ImageView Icon3 = new NinjaLoader()
                                    .type("image")
                                    .url("http://localhost/images/3.png")
                                    .build()
                                    .getImage();

ImageView Icon4 = new NinjaLoader()
                                    .type("image")
                                    .url("http://localhost/images/4.png")
                                    .build()
                                    .getImage();
Pane.getChildren().addAll(Icon1,Icon2,Icon3,Icon4);

so what is the wrong in my code which cause these freezes ?

Thank you ,

Jason4Ever
  • 1,439
  • 4
  • 23
  • 43

2 Answers2

22

You don't need to manage threading at all to load images in a background thread: the Image constructor has a flag to do this. Just do

ImageView icon1 = new ImageView(new Image("http://localhost/images/1.png", true));

etc.

James_D
  • 201,275
  • 16
  • 291
  • 322
4

The issue is that you're running your task in the same thread by calling run method of Runnable:

new LoaderTask().run();

The common practice is to run it in a thread (simple):

Thread th = new Thread(new LoaderTask());
th.setDaemon(true);
th.start();

or to send it to a thread pool (a bit more work). More on this in the related Oracle docs...

UPDATE

If it still freezes:

  • Make sure you don't do any I/O, i.e. reading files or resources in the FX thread, as this would block UI updates an cause the freeze. FX thread is the thread from which you are able to update your UI properties.
  • Try loading resources in a separate thread and update Scene Graph as soon as some/all of them are loaded into memory by running Platform.runLater.
Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • i did it and the same problem , application freeze or slow and wait until all images to be loaded is there any way to call the LoaderTask() from LoaderService() ? i as i read before that Service to obtain data in background thread , but when i tried to do this , i faced the exception "not on FX Application Thread" !. – Jason4Ever Dec 26 '13 at 22:19
  • For non-FX thread have a look at this answer: http://stackoverflow.com/a/12184250/1851024. I'll update my answer in a minute. – Andrey Chaschev Dec 26 '13 at 22:25
  • Thank you very much , your first solution worked very fine but in some places , not all the places :D !! , i tried to use Platform.runLater As Platform.runLater(new NinjaLoaderTask()); and it's worked fine in the places and case which i want !.. – Jason4Ever Dec 26 '13 at 23:43
  • see Answer , it's also did what i want , but my method do it also , but there is a difference , the difference is : at my method "Platform.runLater" thread load all images firstly , then add all once it to the JavaFX Application , but at method , it do sequential thread , which mean load images one by one , and every image loaded added to the JavaFX Application , so how i do this trick in my method ? – Jason4Ever Dec 27 '13 at 00:16