-1

I have two following methods those load my Bitmaps. And when I've tried to load huge image 2000px x 6000px I've got UIfreeze for about 3 seconds. Can anubody help me to improve loading speed of huge images? I's important for me on screen orientation change, because these methods always have been called by that lib and my UI always freezes for about 3 second before rotate...

    /**
     * Async task used to get image details
     */ 
    private static class TilesInitTask implements Runnable {
        private final WeakReference<ScaleImageView> viewRef;
        private final WeakReference<Context> contextRef;
        private final WeakReference<DecoderFactory<? extends ImageRegionDecoder>> decoderFactoryRef;
        private final Uri source;
        private ImageRegionDecoder decoder;
        private Exception exception;

        public TilesInitTask(ScaleImageView view, Context context, DecoderFactory<? extends ImageRegionDecoder> decoderFactory, Uri source) {
            this.viewRef = new WeakReference<>(view);
            this.contextRef = new WeakReference<>(context);
            this.decoderFactoryRef = new WeakReference<DecoderFactory<? extends ImageRegionDecoder>>(decoderFactory);
            this.source = source;
        }

        public void run() {
            ScaleImageView view = null;
            try {
                String sourceUri = source.toString();
                Context context = contextRef.get();
                DecoderFactory<? extends ImageRegionDecoder> decoderFactory = decoderFactoryRef.get();
                view = viewRef.get();
                if (context != null && decoderFactory != null && view != null) {
                    decoder = decoderFactory.make();
                    Point dimensions = decoder.init(context, source);
                    int sWidth = dimensions.x;
                    int sHeight = dimensions.y;

                    int exifOrientation = view.getExifOrientation(sourceUri);
                    if (view.sRegion != null) {
                        sWidth = view.sRegion.width();
                        sHeight = view.sRegion.height();
                    }
                    view.onTilesInited(decoder, sWidth, sHeight, exifOrientation);
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to initialise bitmap decoder", e);
                this.exception = e;
                if (view != null && view.onImageEventListener != null) {
                    view.onImageEventListener.onImageLoadError(exception);
                }
            }
        }
    }

    /**
     * Called by worker task when decoder is ready and image size and EXIF orientation is known.
     */
    private synchronized void onTilesInited(ImageRegionDecoder decoder, int sWidth, int sHeight, int sOrientation) {
        // If actual dimensions don't match the declared size, reset everything.
        if (this.sWidth > 0 && this.sHeight > 0 && (this.sWidth != sWidth || this.sHeight != sHeight)) {
            reset(false);
            if (bitmap != null) {
                if (!bitmapIsCached) {
                    bitmap.recycle();
                }
                bitmap = null;
                bitmapIsPreview = false;
                bitmapIsCached = false;
            }
        }
        this.decoder = decoder;
        this.sWidth = sWidth;
        this.sHeight = sHeight;
        this.sOrientation = sOrientation;
        checkReady();
        checkImageLoaded();
        invalidate();
        requestLayout();
    }

    /**
     * Async task used to load images
     */
    private static class TileLoadTask implements Runnable {
        private final WeakReference<ScaleImageView> viewRef;
        private final WeakReference<ImageRegionDecoder> decoderRef;
        private final WeakReference<Tile> tileRef;
        private Exception exception;

        public TileLoadTask(ScaleImageView view, ImageRegionDecoder decoder, Tile tile) {
            this.viewRef = new WeakReference<>(view);
            this.decoderRef = new WeakReference<>(decoder);
            this.tileRef = new WeakReference<>(tile);
            tile.loading = true;
        }

        public void run() {
            ScaleImageView view = null;
            try {
                view = viewRef.get();
                ImageRegionDecoder decoder = decoderRef.get();
                Tile tile = tileRef.get();
                if (decoder != null && tile != null && view != null && decoder.isReady()) {
                    synchronized (view.decoderLock) {
                        // Update tile's file sRect according to rotation
                        view.fileSRect(tile.sRect, tile.fileSRect);
                        if (view.sRegion != null) {
                            tile.fileSRect.offset(view.sRegion.left, view.sRegion.top);
                        }
                        tile.bitmap = decoder.decodeRegion(tile.fileSRect, tile.sampleSize);
                        tile.loading = false;
                        view.onTileLoaded();
                    }
                } else if (tile != null) {
                    tile.loading = false;
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to decode tile", e);
                this.exception = e;
                if (view != null && view.onImageEventListener != null) {
                    view.onImageEventListener.onTileLoadError(exception);
                }
            }
        }
    }

    /**
     * Called by worker task when a tile has loaded. Redraws the view.
     */
    private synchronized void onTileLoaded() {
        checkReady();
        checkImageLoaded();
        if (isBaseLayerReady() && bitmap != null) {
            if (!bitmapIsCached) {
                bitmap.recycle();
            }
            bitmap = null;
            bitmapIsPreview = false;
            bitmapIsCached = false;
        }
        invalidate();
    }
Make2001
  • 9
  • 2
  • Try this link : http://negativeprobability.blogspot.in/2011/08/lazy-loading-of-images-in-listview.html – KishuDroid Sep 22 '15 at 06:24
  • If you load the image in a separate thread then there should be no UI freeze of any kind. Why are you my using `AsyncTask`? How are you running those `Runnables`? Big thumps up for using `WeakReference` where appropriate, but I am currently in my phone and cannot study your source code in detail. However there still seem to be a few red flags in your code. Like calling `recycle()` on the `Bitmap` or accessing `Views` outside of the main thread. – Xaver Kapeller Sep 22 '15 at 06:35
  • If you show me what kind of images you are trying to load, would be able to provide solution! – Paresh Mayani Sep 22 '15 at 06:47
  • @XaverKapeller, this methods are inside of my View that displays some photos. I call them with .run() method inside that View class – Make2001 Sep 22 '15 at 06:49
  • @XaverKapeller, I'm trying to remake this lib https://github.com/davemorrissey/subsampling-scale-image-view for my needs. When rotate screen on default code image disapears for a second and reloads again for that 3 seconds, I desided to freeze UI to avoid white screen, but now I'm trying to impove loading speed ))) That methods are from SubsamplingScaleImageView (AsynkTasks) – Make2001 Sep 22 '15 at 06:55
  • @PareshMayani, any huge photo (.png, .jpg) with more than 3000 pixels per side – Make2001 Sep 22 '15 at 06:58
  • @Make2001 You don't need a library for that. You just need to fix your code. Freezing the UI is terrible. I can't image why you would do that intentionally!?! Load the image properly and asynchronously and you won't have any problems. – Xaver Kapeller Sep 22 '15 at 07:00
  • @Make2001 And I don't understand how you want to speed up the loading of the image? There is no magical piece of code which makes the InputStreams read the file faster from the disk or wherever you are getting the image from. – Xaver Kapeller Sep 22 '15 at 07:01
  • @Make2001 Load the image asynchronously and show a proper loading indicator, that's all you have to do. – Xaver Kapeller Sep 22 '15 at 07:02
  • @XaverKapeller, I'm new in Android and programming and I dont know how to do that ((( So, I asked for help here. Those methods are part of that library, I'm using it because it has good functionality that I need – Make2001 Sep 22 '15 at 07:03
  • @Make2001 Then at least use a proper library like [**Picasso**](http://square.github.io/picasso/). But especially if you are new to Android and/or Java you are not going to learn anything by using libraries even for the simplest tasks. What you are trying to do is just a few lines of code. You should really try it for yourself. – Xaver Kapeller Sep 22 '15 at 07:06
  • By replacing AsyncTasks and moving calls like `view.onTilesInited` out of onPostExecute, you are either calling view methods off the UI thread, or running your runnable in the UI thread. The latter seems most likely given the UI pauses. The original library does no CPU intensive work on the UI thread and has no UI pauses while loading images. Because orientation changes destroy the layout, a new view is created in the new layout and it has to load the image. If your images are too large to load into memory, a loader like Picasso won't help. You can make it appear faster using a preview image. – Dave Morrissey Sep 25 '15 at 18:54

1 Answers1

-1

Working with bitmaps and pictures is a sensitive operation. I suggest you to use a library for these kind of operations suces as

https://github.com/nostra13/Android-Universal-Image-Loader

http://square.github.io/picasso/

Edit:

Here you can how to add and init library. Correct usage of Universal Image Loader

After you init with best configuration you can use file paths below which is suitable for your operation:

"http://example.com/image.png" // from Web
"file:///mnt/sdcard/image.png" // from SD card
"file:///mnt/sdcard/video.mp4" // from SD card (video thumbnail)
"content://media/external/images/media/13" // from content provider
"content://media/external/video/media/13" // from content provider (video thumbnail)
"assets://image.png" // from assets
"drawable://" + R.drawable.img // from drawables (non-9patch images)

And finally you can load it with:

ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
imageLoader.displayImage(imageUri, imageView);

Good luck.

Community
  • 1
  • 1
savepopulation
  • 11,736
  • 4
  • 55
  • 80