4

I'm having an error using a custom class that inherits from LruCache in Android. It's supposed to download images and cache them; yesterday, it was working, but this morning I run into this issue.

This is the code of the class:

public class LruMemoryCache extends LruCache<String, Bitmap> {
    private final Context context;
    private static LruMemoryCache instance;

    private LruMemoryCache(Context context) {
//      super(1024 * 1024 * (((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass()) / 8);
        super(5 * 1024 * 1024);
        this.context = context;
    }

    public static synchronized LruMemoryCache getInstance(Context context) {
        if (instance == null) {
            instance = new LruMemoryCache(context);

        }
        return instance;
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getByteCount();
    }

    public void loadBitmap(String url, ImageView imageView) {
        final String imageKey = url;
        final Bitmap bitmap = get(imageKey);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            task.execute(url);
        }
    }

    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
        Bitmap myBitmap;
        ImageView mImageView;

        public BitmapWorkerTask(ImageView imageView) {
            mImageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                URL url = new URL("http://www.google.com/logos/classicplus.png");
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoInput(true);
                connection.connect();
                InputStream input = connection.getInputStream();
                myBitmap = BitmapFactory.decodeStream(input);
            } catch (IOException e) {
                e.printStackTrace();
            }

            put(params[0], myBitmap);
            return myBitmap;
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            super.onPostExecute(result);
            mImageView.setImageBitmap(result);
        }
    }
}

And this is the error from the LogCat:

05-15 08:02:08.639: E/AndroidRuntime(12818): FATAL EXCEPTION: AsyncTask #2
05-15 08:02:08.639: E/AndroidRuntime(12818): java.lang.RuntimeException: An error occured while executing doInBackground()
05-15 08:02:08.639: E/AndroidRuntime(12818):    at android.os.AsyncTask$3.done(AsyncTask.java:200)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.lang.Thread.run(Thread.java:1019)
05-15 08:02:08.639: E/AndroidRuntime(12818): Caused by: java.lang.NoSuchMethodError: getByteCount
05-15 08:02:08.639: E/AndroidRuntime(12818):    at com.xs2theworld.sundio.caching.LruMemoryCache.sizeOf(LruMemoryCache.java:36)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at com.xs2theworld.sundio.caching.LruMemoryCache.sizeOf(LruMemoryCache.java:1)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at android.support.v4.util.LruCache.safeSizeOf(LruCache.java:230)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at android.support.v4.util.LruCache.put(LruCache.java:123)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at com.xs2theworld.sundio.caching.LruMemoryCache$BitmapWorkerTask.doInBackground(LruMemoryCache.java:72)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at com.xs2theworld.sundio.caching.LruMemoryCache$BitmapWorkerTask.doInBackground(LruMemoryCache.java:1)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at android.os.AsyncTask$2.call(AsyncTask.java:185)
05-15 08:02:08.639: E/AndroidRuntime(12818):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
05-15 08:02:08.639: E/AndroidRuntime(12818):    ... 4 more

Any idea why this could be happening?

Thanks a lot in advance everybody.

EDIT: I just realized that this error doesn't happen with Google Galaxy Nexus S phone, but it does with Samsun Galaxy.. anybody knows why?

noloman
  • 11,411
  • 20
  • 82
  • 129

3 Answers3

6

The problem is caused by:

@Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getByteCount();
    }

The method getByteCount() can only be used with an API >= 12, and Samsung Galaxy uses the API 10.

Solution:

@Override
    protected int sizeOf(String key, Bitmap value) {
        if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12)
            return value.getByteCount();
        else
            return (value.getRowBytes() * value.getHeight());
    }
noloman
  • 11,411
  • 20
  • 82
  • 129
1

for LruCache , use the support library of google , or get the original source code of it .

usually , it's quite sophisticated to understand what they did on the source code , but this time it's quite easy .

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • i see , so you should the code of android . here's something that worked for me after a few tweaking (change the map.eldest() part) : http://www.oschina.net/code/explore/android-4.0.1/core/java/android/util/LruCache.java – android developer May 15 '12 at 11:05
  • @androiddeveloper can you please tell me how you manage to get rid off error generated by `map.eldest()` – Hunt Aug 23 '12 at 08:35
  • 2
    sure . either override the removeEldestEntry() for the LinkedHashMap , or use cacheMap.keySet().iterator().next() , which will return you the eldest key . then you can get its value (if you need) and remove the key from the LinkedHashMap . – android developer Aug 23 '12 at 10:53
  • 1
    @androiddeveloper i have replaced `Map.Entry toEvict = map.eldest();` by `Map.Entry toEvict = (Entry) map.keySet().iterator().next();` i hope that is enough ? – Hunt Aug 23 '12 at 14:05
1

I tried all of the above methods and they were close, but not quite right (for my situation at least).

I was using bitmap.getByteCount(); inside of the sizeOf() method when creating a new LruCache:

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
        return bitmap.getByteCount();
    }
};  

I then tried the suggested:

return bitmap.getRowBytes() * bitmap.getHeight();

This was great, but I noticed that the returned values were different and when I used the suggestion above, it would not even make a cache on my device. I tested the return values on a Nexus One running api 3.2 and a Galaxy Nexus running 4.2:

bitmap.getByteCount(); returned-> 15  
bitmap.getRowBytes() * bitmap.getHeight(); returned-> 15400

So to solve my issue, I simply did this:

return (bitmap.getRowBytes() * bitmap.getHeight()) / 1000; 

instead of:

return bitmap.getByteCount();  

May not be the same situation you were in, but this worked for me.

levibostian
  • 753
  • 1
  • 7
  • 21
  • everywhere I look, every source I read, all I see it `return (bitmap.getRowBytes() * bitmap.getHeight()) / 1000;`. It must be the type of file I am saving or something. So next time I need to use `bitmap.getByteCount();`, I will do some testing to see what value I need first. – levibostian Jun 06 '13 at 21:42