【问题标题】:Out of memory cache error when accessing inside the app在应用程序内部访问时出现内存缓存错误
【发布时间】:2023-04-06 15:11:03
【问题描述】:

我搜索了很多,但我不明白我的错误在哪里。 首先在我的应用程序中,如果没有网络,我将从网络获取图像,我从创建的数据库中获取它们。 我将发布我的 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){}
}

}

【问题讨论】:

    标签: android out-of-memory heap-memory


    【解决方案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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-27
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多