在应用程序内部访问时出现内存缓存错误

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&lt;k,v&gt; !!!

地图将永远保留对图像视图(保留活动上下文)和位图的引用,这是您有这些内存损失的主要原因之一。进一步对上下文的引用是将您的整个活动永远保存在内存中。都是非常糟糕的想法。

您将使用 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/…

以上是关于在应用程序内部访问时出现内存缓存错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 QNetworkManager 时出现内存访问错误

HTTP 错误 500.19 - 在 IIS 上托管时出现内部服务器错误 [重复]

仅在从 Windows 运行时出现 500 内部错误 (7)

消息:尝试访问 aws gateway api 时出现“内部服务器错误”

访问共享进程内存时出现分段错误(核心转储)

访问 $GITLAB/admin/runners 时出现内部服务器错误 500