NoSuchMethodError при использовании LruCache в Android

У меня возникает ошибка при использовании пользовательского класса, наследуемого от LruCache в Android. Предполагается загружать изображения и кэшировать их; вчера это работало, но сегодня утром я столкнулся с этой проблемой.

Это код класса:

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);
        }
    }
}

И это ошибка от 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

Любая идея, почему это может происходить?

Заранее всем большое спасибо.

РЕДАКТИРОВАТЬ: я только что понял, что эта ошибка не возникает с телефоном Google Galaxy Nexus S, но она возникает с Samsung Galaxy ... кто-нибудь знает, почему?


person noloman    schedule 15.05.2012    source источник
comment
как вы решили проблему, так как я все еще получаю ошибку времени компиляции на getByteCount()   -  person Hunt    schedule 23.08.2012
comment
@Hunt проверьте мой комментарий ниже, принятый ответ   -  person noloman    schedule 23.08.2012


Ответы (3)


Проблема вызвана:

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

Метод getByteCount() можно использовать только с API >= 12, а Samsung Galaxy использует API 10.

Решение:

@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());
    }
person noloman    schedule 15.05.2012

для LruCache используйте библиотеку поддержки Google или получите оригинальный исходный код.

обычно довольно сложно понять, что они сделали с исходным кодом, но на этот раз это довольно просто.

person android developer    schedule 15.05.2012
comment
я вижу, так что вы должны код андроида. вот что у меня сработало после нескольких настроек (изменить часть map.eldest()): oschina.net/code/explore/android-4.0.1/core/java/android/util/ - person android developer; 15.05.2012
comment
@androiddeveloper, не могли бы вы рассказать мне, как вам удается избавиться от ошибки, сгенерированной map.eldest() - person Hunt; 23.08.2012
comment
Конечно . либо переопределите removeEldestEntry() для LinkedHashMap, либо используйте cacheMap.keySet().iterator().next(), который вернет вам самый старый ключ. затем вы можете получить его значение (если вам нужно) и удалить ключ из LinkedHashMap. - person android developer; 23.08.2012
comment
@androiddeveloper я заменил Map.Entry<K, V> toEvict = map.eldest(); на Map.Entry<K, V> toEvict = (Entry<K, V>) map.keySet().iterator().next(); надеюсь этого достаточно? - person Hunt; 23.08.2012

Я попробовал все вышеперечисленные методы, и они были близки, но не совсем правильны (по крайней мере, для моей ситуации).

Я использовал bitmap.getByteCount(); внутри метода sizeOf() при создании нового LruCache:

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

Затем я попробовал предложенное:

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

Это было здорово, но я заметил, что возвращаемые значения были другими, и когда я использовал приведенное выше предложение, он даже не создавал кеш на моем устройстве. Я протестировал возвращаемые значения на Nexus One с API 3.2 и Galaxy Nexus с 4.2:

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

Итак, чтобы решить мою проблему, я просто сделал это:

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

вместо:

return bitmap.getByteCount();  

Возможно, это не та ситуация, в которой вы были, но это сработало для меня.

person levibostian    schedule 06.06.2013
comment
куда бы я ни посмотрел, в каждом источнике, который я прочитал, все, что я увидел, это return (bitmap.getRowBytes() * bitmap.getHeight()) / 1000;. Это должен быть тип файла, который я сохраняю, или что-то в этом роде. Поэтому в следующий раз, когда мне нужно будет использовать bitmap.getByteCount();, я проведу некоторое тестирование, чтобы увидеть, какое значение мне нужно в первую очередь. - person levibostian; 07.06.2013