在Android中使用LruCache出现NoSuchMethodError错误

4
我是一名能翻译文本的助手。
以下是您需要翻译的内容,涉及IT技术。请注意,我将保留HTML标记,并且尽可能使文本易于理解,但不会提供任何解释。

我在Android中使用继承自LruCache的自定义类时遇到了错误。

该类应该下载并缓存图像;昨天它还能正常工作,但今天我遇到了这个问题。

以下是该类的代码:

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手机上,但是在Samsun Galaxy手机上会发生.. 有人知道为什么吗?

3个回答

6

问题的原因是:

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

方法getByteCount()只能在API >= 12的情况下使用,而三星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());
    }

1

对于 LruCache,可以使用 Google 的支持库,或获取其原始源代码。

通常来说,理解源代码中的内容相当复杂,但这次却很容易。


我明白了,所以你应该看一下 Android 的代码。这是经过一些调整后对我有用的东西(更改 map.eldest() 部分):http://www.oschina.net/code/explore/android-4.0.1/core/java/android/util/LruCache.java - android developer
@androiddeveloper,你能告诉我如何解决由map.eldest()生成的错误吗? - Hunt
2
可以通过覆盖 LinkedHashMap 的 removeEldestEntry() 方法或使用 cacheMap.keySet().iterator().next() 方法来获取最老的键,然后您可以获取其值(如果需要)并从 LinkedHashMap 中删除该键。 - android developer
1
@androiddeveloper 我已经将 Map.Entry<K, V> toEvict = map.eldest(); 替换为 Map.Entry<K, V> toEvict = (Entry<K, V>) map.keySet().iterator().next();,希望这样就足够了? - Hunt

1
我尝试了以上所有方法,它们都接近正确,但对我的情况来说还不够完美。
当我创建一个新的 LruCache 时,在 sizeOf() 方法中我使用了 bitmap.getByteCount();
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
        return bitmap.getByteCount();
    }
};  

我尝试了建议的方法:

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

这很棒,但我注意到返回的值不同,当我使用上面的建议时,它甚至不会在我的设备上进行缓存。我在运行api 3.2的Nexus One和运行4.2的Galaxy Nexus上测试了返回值:
bitmap.getByteCount(); returned-> 15  
bitmap.getRowBytes() * bitmap.getHeight(); returned-> 15400

因此,为了解决我的问题,我只需这样做:

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

代替:

return bitmap.getByteCount();  

也许你的情况不同,但这对我很有效。

无论我看哪里,阅读哪个来源,我都看到return (bitmap.getRowBytes() * bitmap.getHeight()) / 1000;。这一定是我正在保存的文件类型或其他原因。所以下次我需要使用bitmap.getByteCount();时,我会先进行一些测试,以确定我需要什么值。 - levibostian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接