1

i'm trying to implement a image gallery, which should show ~ 5-15 smaller images and one "current selected" bigger image.

It looks like: http://www.mobisoftinfotech.com/blog/wp-content/uploads/2012/06/galleryDemo.png

I've looked up many sources and now decided to use a bitmap-cache (lru-cache) (thanks to a person from this forum!).

I don't get memory-leaks at the moment, but i'm not happy with this solution because everytime i scroll, some images are removed from the cache and i've to reload them... so the user has to wait for reloading images... It's really annoying to wait 0.5 - 1 second every time i scroll to the other side...

public static WeakReference<Bitmap> getBitmap(String imageName, int width,
        int height) {
    if (!imageName.contains(".jpg")){
        imageName += ".jpg";
    }
    String pathToImage = getPathToImage(imageName);
    Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(pathToImage, options);

    /*
     * Calculate inSampleSize
     */
    options.inSampleSize = calculateInSampleSize(options, width, height);

    /*
     * Removes the alpha-channel from bitmap (not necessary)
     */
    options.inPreferredConfig = Bitmap.Config.RGB_565;

    /*
     * Decode bitmap with inSampleSize set
     */
    options.inJustDecodeBounds = false;

    WeakReference<Bitmap> scaledBitmap = new WeakReference<Bitmap>(
            BitmapFactory.decodeFile(pathToImage, options));
    return scaledBitmap;

Is it possible that i make a mistake in getting the images? At the moment i'll use 320x480 resolution for the single-selected image and 64x64 for the list on the bottom...

Really weird is the fact, that it doesn't matter, how big the resolutions of the images are, the lru-cache will still remove some images, even if i choose only 64x64 images... I might be doing wrong something with the lru cache?

The following code shows my implementation:

    /*
     * Bitmaps which should be shown
     */
    mMemoryCache = (BitmapCache) getLastNonConfigurationInstance();
    if (mMemoryCache == null) {
        // Get max available VM memory, exceeding this amount will throw an
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an
        // int in its constructor.
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        // Use 1/4th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 2; // TODO default 8

        mMemoryCache = new BitmapCache(cacheSize);
    }

And the Class BitmapCache:

public BitmapCache(int maxSize, Resources resources) {
    super(maxSize);
    this.resources = resources;
}

@Override
protected int sizeOf(String key, AsyncDrawable value) {
    if (value.getBitmap() != null) {
        /* 
         * The cache size will be measured in kilobytes rather than
         * number of items.
         */
        return value.getBitmap().getRowBytes()
                * value.getBitmap().getHeight() / 1024;
    }
    return super.sizeOf(key, value);
}

public void loadBitmap(Picture picture, ImageView mImageView,
        ImageResolution resolution) {
    if (cancelPotentialWork(picture.getFileName(), //  + resolution.name()
            mImageView)) {
        final BitmapWorkerTask task = new BitmapWorkerTask(this,
                mImageView, picture, resolution);
        final AsyncDrawable asyncDrawable = new AsyncDrawable(resources,
                null, task);
        mImageView.setImageDrawable(asyncDrawable);
        task.execute();
    }
}

Thank you very much :(

Some more code: BitmapWorkerTask:

@Override
protected Bitmap doInBackground(Void... params) {
    /*
     *  Decode image in background.
     */
    final Bitmap bitmap = FileHandler.getBitmap(
            picture.getPictureId() + "", resolution).get();
    return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
    /*
     *  Once complete, see if ImageView is still around and set bitmap.
     */
    if (isCancelled()) {
        bitmap = null;
    }

    if (imageViewReference != null && bitmap != null) {
        final ImageView imageView = imageViewReference.get();

        if (imageView != null) {
            final Drawable drawable = imageView.getDrawable();
            if (drawable instanceof AsyncDrawable) {
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                final BitmapWorkerTask bitmapWorkerTask = asyncDrawable
                        .getBitmapWorkerTask();
                if (this == bitmapWorkerTask && imageView != null) {
                    imageView.setImageBitmap(bitmap);
                    bitmapCache.addBitmapToMemoryCache(
                            picture.getFileName(), // + resolution.name()
                            asyncDrawable);
                }
            }
        }
    }
}
Frame91
  • 3,670
  • 8
  • 45
  • 89
  • 1
    Is it possible to get the nearest neighbors of the image being current ly viewed and cache them.. Just throwing an idea out there – smk Feb 08 '13 at 15:40
  • Hey, thanks for the idea! It is possible to get the nearest neighbours ;) I think this can be a good idea to load them... but that doesn't solve some "fast" scrolling action, where the user just slides from the one side to the other... I think other image-showing gallerys have solved this problem... like whatsapp - in the group-media-activity i can scroll as fast as i want and there is no reloading of images required (or the reloading is somehow far better implemented then mine) – Frame91 Feb 08 '13 at 15:44
  • 1
    Is it possible that it's not a problem of image loading, but of cell recycling ? I mean, when you fast scroll with an adapter, a cell not used on the right will be used on the left (so that it don't inflate everything). So, what would happen if you fast scroll to the left ? It will probably launch 5 async task in a row, but reuse the same cell 2 times. So what happen when 3 async tasks try to set the image ? – Redwarp Feb 09 '13 at 16:45
  • Or it could be also a problem of concurrency ? Like several async tasks trying to access to the lru cache at the same time ? – Redwarp Feb 09 '13 at 16:46
  • Hey, i'm using "asyncdrawable" in which every drawable has an bitmapworker task. My cache sees, if a current asynctask is running and cancels it, when another task starts... I don't think, the reloading is the problem. What i don't understand is the fact, why the cache is removing some bitmaps... or how whatsapp or other applications can show many more images at the same time. I've added more code to my example. Can you take a short look? – Frame91 Feb 09 '13 at 19:40
  • Is it possible, that the gallery of android is the wrongdoer? I've tested it with 2x2 size pictures and scrolling very fast from left to the right has the same effect... the picture is shown after 0.5 seconds, so maybe the gallery is not able to load images at runtime? ... or do i have some errors in my asncy-task controlling – Frame91 Feb 09 '13 at 20:24

1 Answers1

0

AWESOME! I found the mistake why it couldn't work. The fault was in my LruCache, which stored AsyncDrawables, what is totally dumb. Everytime sizeOf in the LruCache was called, the bitmap inside of the asyncdrawable was null, so he just returned super.sizeOf(key, value).

Now it works like a charm!! :)

Thanks for your helps!

Frame91
  • 3,670
  • 8
  • 45
  • 89