Loading sprites from URL on separate thread

  • Zlatan Momic

    Zlatan Momic - 2011-12-01

    I have tried to load sprites on separate thread. So when ImageIO tries to read sprite from URL, client would not hang for a moment.

    Idea is to load and return failsafe sprite first and after background thread finishes download it adds fresh sprite to cache.

                    public Sprite getSprite(final String ref) {
                        final SpriteCache cache = SpriteCache.get();
                        // try to get cashed sprite
                        Sprite sprite = cache.get(ref);
                        // if there is no cashed sprite 
                        if (sprite == null) {
                            // load failsafe while waiting for sprite to be loaded from URL
                            sprite = loadSprite(FAILSAFE_ICON_REF);
                            Thread thread = new Thread() {
                                public void run() {
                                    try {
                                    } catch (InterruptedException ex) {}
                                    // load and add sprite to cache
                                    Sprite sprite = loadSprite(ref);
                                    if (sprite != null) {
                                        cache.add(ref, sprite);
                        return sprite;

    Thread is executing properly but changes to cache are not visible. It's like entities and gui components needs to be refreshed from cache.

    What am I missing here?
    If someone have any idea how to do this better, please help.

  • Kimmo Rundelin

    Kimmo Rundelin - 2011-12-01

    Pretty much everything (entities, maps, etc) loads their sprites only once, so it never gets refreshed from the cache. One possibility could be handling it in the ImageSprite by changing the internal image reference in the background thread. This would still be a problem for derived sprites, so ImageSprite should still probably block drawing until the correct image has been loaded. So it uncertain if that would have any positive effect (the client already tries to load most sprites in the game loop thread, which spends most of its time sleeping).

  • Katie Russell

    Katie Russell - 2011-12-01

    If you want to load the images with a delay, some of the methods provided in http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ScheduledExecutorService.html may be a cleaner way of doing it - you can schedule tasks with delays or to repeat at a fixed rate. There is a nice example in the doc. It also avoids creating a new thread for each sprite?

  • Zlatan Momic

    Zlatan Momic - 2011-12-05

    Well, that did it. Changing reference will initiate IEntity.PROP_CLASS change and representation change of entity?

    After background thread finishes download and after Entity's reference has changed, should I add it to cache again? Looks like client works the same regardless of adding it to cache again.

    Here is the code. If you have any suggestion please comment it…

    SpriteStore.java method loadSprite(final String ref)

        if (ref.startsWith("http://")) {
            logger.info("Loading sprite from a URL...");
            final ImageSprite sprite = new ImageSprite(getFailsafe(), FAILSAFE_ICON_REF, ref);
            return sprite;


        public ImageSprite(final Sprite sprite, final String reference, final String newReference) {
            this(sprite, reference);
            this.newReference = newReference;
        public void loadInBackground() {
            if (newReference == null) 
            Thread t = new Thread(downloader);
        private final Runnable downloader = new Runnable() {
            public void run() {
                BufferedImage sourceImage = null;
                try {
                    URL url = new URL(newReference.toString());
                    sourceImage = ImageIO.read(url);
                } catch (IOException ex) {
                    logger.error("Loading sprite from URL failed: " + newReference, ex);
                if (sourceImage == null) {
                    logger.error("Loading sprite from URL failed: " + newReference);
                // update sprite image and reference
                ImageSprite.this.image = sourceImage;
                ImageSprite.this.reference = newReference;
                // adding it to cache, DO I NEED THIS???
                final SpriteCache cache = SpriteCache.get();
                cache.add(newReference, ImageSprite.this);
  • Kimmo Rundelin

    Kimmo Rundelin - 2011-12-07

    EntityViews do not get any notice, but the next time the sprite is drawn the new image is used. For purposes where the size of the sprite matters, the sprite will likely need an appropriate notifier interface.

    Caching the sprite again is not necessary, as the sprite reference has not changed, just its internal state.

    I'd also consider using executors, like kymara suggested (ThreadPoolExecutor, probably), to have a sane amount of download threads in case many images need to be downloaded simultaneously.



Cancel  Add attachments