在应用程序内部访问时出现内存缓存错误
Posted
技术标签:
【中文标题】在应用程序内部访问时出现内存缓存错误【英文标题】:Out of memory cache error when accessing inside the app 【发布时间】:2012-11-16 08:40:37 【问题描述】:我搜索了很多,但我不明白我的错误在哪里。 首先在我的应用程序中,如果没有网络,我将从网络获取图像,我从创建的数据库中获取它们。 我将发布我的 ImageLoader 类,然后是内存类,然后是 utils 类,如果有问题,我需要一些帮助,提前谢谢。
public class ClassImageLoader
ClassMemoryCache memoryCache=new ClassMemoryCache();
ClassFileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
public ClassImageLoader(Context context)
fileCache=new ClassFileCache(context);
executorService=Executors.newFixedThreadPool(5);
final int stub_id=R.drawable.restlogobutton;
public void DisplayImage(String url, ImageView imageView)
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
private void queuePhoto(String url, ImageView imageView)
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
private Bitmap getBitmap(String url)
File f=fileCache.getFile(url);
//from SD cache
Bitmap b = decodeFile(f);
if(b!=null)
return b;
//from web
try
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
ClassUtils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
catch (Exception ex)
ex.printStackTrace();
return null;
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f)
try
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true)
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
catch (FileNotFoundException e)
return null;
//Task for the queue
private class PhotoToLoad
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i)
url=u;
imageView=i;
class PhotosLoader implements Runnable
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad)
this.photoToLoad=photoToLoad;
public void run()
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
boolean imageViewReused(PhotoToLoad photoToLoad)
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p)bitmap=b;photoToLoad=p;
public void run()
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
public void clearCache()
memoryCache.clear();
fileCache.clear();
public class ClassMemoryCache
private Map<String, Bitmap> cache=Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes
public ClassMemoryCache()
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
public void setLimit(long new_limit)
limit=new_limit;
public Bitmap get(String id)
if(!cache.containsKey(id))
return null;
return cache.get(id);
public void put(String id, Bitmap bitmap)
try
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
catch(Throwable th)
th.printStackTrace();
private void checkSize()
if(size>limit)
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext())
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
public void clear()
cache.clear();
long getSizeInBytes(Bitmap bitmap)
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
public class ClassUtils
public static void CopyStream(InputStream is, OutputStream os)
final int buffer_size=1024;
try
byte[] bytes=new byte[buffer_size];
for(;;)
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
catch(Exception ex)
【问题讨论】:
【参考方案1】:内存管理是一个相当广泛和高级的话题,但我会用大写字母发布一个非常重要的提示:
DO NOT USE MAP<k,v> !!!
地图将永远保留对图像视图(保留活动上下文)和位图的引用,这是您有这些内存损失的主要原因之一。进一步对上下文的引用是将您的整个活动永远保存在内存中。都是非常糟糕的想法。
您将使用 LruCache(可在兼容性库中获得)来缓存位图,并只让活动保持对图像视图的引用(来自 XML 的静态或使用适配器动态)
这是一个 Google IO 视频 http://www.youtube.com/watch?v=gbQb1PVjfqM,来自 Google 的人自己展示了该领域的一些最佳实践,他们在 4 分钟内展示了 LruCache 的用法。
【讨论】:
这个视频很完美,但我仍然是 android 的初学者,我明白了他所说的,但我不知道在我的代码中具体在哪里修改,因为我的课程是混合的,但谢谢你真的帮助了【参考方案2】:// 在您的代码中替换此方法并尝试我不确定它是否会解决您的错误,但您可以尝试一下。
private Bitmap getBitmap(String url)
//I identify images by hashcode. Not a perfect solution, good for the demo.
String filename=String.valueOf(url.hashCode());
File f=new File(cacheDir, filename);
//from SD cache
Bitmap b = decodeFile(f);
if(b!=null)
return b;
//from web
try
Bitmap bitmap=null;
InputStream is=new URL(url).openStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
catch (Exception ex)
ex.printStackTrace();
return null;
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f)
try
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true)
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inTempStorage = new byte[32*1024];
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
catch (FileNotFoundException e)
return null;
查看此链接以获取 ImageLoaderClass: http://code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/lazylist/ImageLoader.java
OOM 问题检查此链接: Strange out of memory issue while loading an image to a Bitmap object
如果您仍然遇到错误,请检查我在多次搜索后找到并解决了我的问题的这个链接: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/vYWKY1Y6bUo
【讨论】:
imageLoader 在哪个类中替换了你的代码而不是我的所有代码? 我在 File f=new File(cacheDir, filename) 上遇到错误;在 cacheDir 为什么是兄弟? code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/… 检查此链接以获取 ImageLoaderClass,如果仍然出现错误,请检查此链接:***.com/questions/477572/…以上是关于在应用程序内部访问时出现内存缓存错误的主要内容,如果未能解决你的问题,请参考以下文章
HTTP 错误 500.19 - 在 IIS 上托管时出现内部服务器错误 [重复]
仅在从 Windows 运行时出现 500 内部错误 (7)