关于android中的图片缓冲区问题

来源:互联网 发布:取消地级市 知乎 编辑:程序博客网 时间:2024/05/17 00:18

关于android中的图片缓冲区问题

    android中的图片缓冲区一直是个问题,包括超出虚拟机所分配的资源上限,重用已回收的bitmap等等,解决这个问题,每个人有每个人不同的方式,这里记录下我在项目中学到的的图片缓冲区的实现方式。

    一个bitmap,可以携带一个属性,标识着这个bitmap最后的使用时间。而我们如果创建一个缓冲区,这个区里的bitmap数量是有一定限制的,否则就会出现内存溢出,超出了虚拟机分给程序的内存空间。而这个bitmap的最后使用时间就是确定删不删除这个bitmap的标志。

   
Java代码  收藏代码
  1. /** 
  2.     * A Bitmap associated with its last modification date. This can be used to check 
  3.     * whether the book covers should be downloaded again. 
  4.     */  
  5.    public static class ExpiringBitmap {  
  6.        public Bitmap bitmap;  
  7.        public Calendar lastModified;  
  8.    }  

   
    通过url去得到bitmap这个方法基本都是一致的,也许有的人写的强壮一点,功能会多一些。下边这个添加了附带cookies。其实多数时候是用不到的。

Java代码  收藏代码
  1. /** 
  2.     * Loads an image from the specified URL with the specified cookie. 
  3.     * 
  4.     * @param url The URL of the image to load. 
  5.     * @param cookie The cookie to use to load the image. 
  6.     * 
  7.     * @return The image at the specified URL or null if an error occured. 
  8.     */  
  9.    public static ExpiringBitmap load(String url, String cookie) {  
  10.        ExpiringBitmap expiring = new ExpiringBitmap();  
  11.   
  12.        final HttpGet get = new HttpGet(url);  
  13.        if (cookie != null) get.setHeader("cookie", cookie);  
  14.   
  15.        HttpEntity entity = null;  
  16.        try {  
  17.            final HttpResponse response = HttpManager.execute(get);  
  18.            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
  19.                setLastModified(expiring, response);  
  20.   
  21.                entity = response.getEntity();  
  22.   
  23.                InputStream in = null;  
  24.                OutputStream out = null;  
  25.   
  26.                try {  
  27.                    in = entity.getContent();  
  28.                    if (FLAG_DECODE_BITMAP_WITH_SKIA) {  
  29.                        expiring.bitmap = BitmapFactory.decodeStream(in);  
  30.                    } else {  
  31.                        final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();  
  32.                        out = new BufferedOutputStream(dataStream, IOUtilities.IO_BUFFER_SIZE);  
  33.                        IOUtilities.copy(in, out);  
  34.                        out.flush();  
  35.   
  36.                        final byte[] data = dataStream.toByteArray();  
  37.                        expiring.bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);  
  38.                    }  
  39.                } catch (IOException e) {  
  40.                    android.util.Log.e(LOG_TAG, "Could not load image from " + url, e);  
  41.                } finally {  
  42.                    IOUtilities.closeStream(in);  
  43.                    IOUtilities.closeStream(out);  
  44.                }  
  45.            }  
  46.        } catch (IOException e) {  
  47.            android.util.Log.e(LOG_TAG, "Could not load image from " + url, e);  
  48.        } finally {  
  49.            if (entity != null) {  
  50.                try {  
  51.                    entity.consumeContent();  
  52.                } catch (IOException e) {  
  53.                    android.util.Log.e(LOG_TAG, "Could not load image from " + url, e);  
  54.                }  
  55.            }  
  56.        }  
  57.   
  58.        return expiring;  
  59.    }  



    在上边的方法里,可以看到有一个setLastModified(expiring, response);这个方法就是给bitmap这个数据添加上最后的修改时间,默认的会赋值成下载完成的时间,当二次访问的时候,我们可以重新赋值新的时间点。
   
   
Java代码  收藏代码
  1. private static void setLastModified(ExpiringBitmap expiring, HttpResponse response) {  
  2.         expiring.lastModified = null;  
  3.   
  4.         final Header header = response.getFirstHeader("Last-Modified");  
  5.         if (header == nullreturn;  
  6.           
  7.         if (sLastModifiedFormat == null) {  
  8.             sLastModifiedFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");  
  9.         }  
  10.   
  11.         final Calendar calendar = GregorianCalendar.getInstance();  
  12.         try {  
  13.             calendar.setTime(sLastModifiedFormat.parse(header.getValue()));  
  14.             expiring.lastModified = calendar;  
  15.         } catch (ParseException e) {  
  16.             // Ignore  
  17.         }  
  18.     }  


     从缓冲区中获取bitmap,这个方法基本都是给外部调用的,如果缓冲区存在这个bitmap,会返回,如果不存在就调用load(url)方法去获得这个bitmap数据并且放入缓冲区中。

   
Java代码  收藏代码
  1. /** 
  2.     * Retrieves a drawable from the book covers cache, identified by the specified id. 
  3.     * If the drawable does not exist in the cache, it is loaded and added to the cache. 
  4.     * If the drawable cannot be added to the cache, the specified default drwaable is 
  5.     * returned. 
  6.     * 
  7.     * @param id The id of the drawable to retrieve 
  8.     * @param defaultCover The default drawable returned if no drawable can be found that 
  9.     *         matches the id 
  10.     * 
  11.     * @return The drawable identified by id or defaultCover 
  12.     */  
  13.    public static FastBitmapDrawable getCachedCover(String id, FastBitmapDrawable defaultCover) {  
  14.        FastBitmapDrawable drawable = null;  
  15.   
  16.        SoftReference<FastBitmapDrawable> reference = sArtCache.get(id);  
  17.        if (reference != null) {  
  18.            drawable = reference.get();  
  19.        }  
  20.   
  21.        if (drawable == null) {  
  22.            final Bitmap bitmap = loadCover(id);  
  23.            if (bitmap != null) {  
  24.                drawable = new FastBitmapDrawable(bitmap);  
  25.            } else {  
  26.                drawable = NULL_DRAWABLE;  
  27.            }  
  28.   
  29.            sArtCache.put(id, new SoftReference<FastBitmapDrawable>(drawable));  
  30.        }  
  31.   
  32.        return drawable == NULL_DRAWABLE ? defaultCover : drawable;  
  33.    }  


    在文件的一开始,我们需要生成一个静态的缓冲区,一般都采用HashMap。

   
Java代码  收藏代码
  1. private static final HashMap<String, SoftReference<FastBitmapDrawable>> sArtCache =  
  2.             new HashMap<String, SoftReference<FastBitmapDrawable>>();  


    从缓冲区中删除文件,从一个好的思路上来说,应该是对于缓冲区中所有文件进行一个对比,把那些长时间没有用到的文件删除。这给出的方法比较简单,就是删除所有的缓冲区文件。
   
   
Java代码  收藏代码
  1. /** 
  2.      * Removes all the callbacks from the drawables stored in the memory cache. This 
  3.      * method must be called from the onDestroy() method of any activity using the 
  4.      * cached drawables. Failure to do so will result in the entire activity being 
  5.      * leaked. 
  6.      */  
  7.     public static void cleanupCache() {  
  8.         for (SoftReference<FastBitmapDrawable> reference : sArtCache.values()) {  
  9.             final FastBitmapDrawable drawable = reference.get();  
  10.             if (drawable != null) drawable.setCallback(null);  
  11.         }  
  12.     }  


    这样一个简单的缓冲区文件基本雏形就有了,其他的需要自己按照自己的需求去写一下方法。或者进行一些有话,比如说这里提出的思路是每次读取一个bitmap时都会重置他的lastModified这个属性,然后在删除时,我们对这个属性进行遍历,删掉低于平均访问水瓶的bitmap,当然这个在以上程序代码中没有明确代码,因为以上代码是为了各自不同需求的基本代码,可以自己进行修改。对于前边这个思路的优化,可以删除掉lastModified这个属性,而采用一个队列的缓冲区方式,每次访问,都把这个bitmap先从队列中取出来,也就是删除,然后添加到队尾,这样的思路好处在于,当缓冲区多大,需要删除掉一些图片时,可以直接删除队首的若干个元素,节约了遍历所有元素的时间。

    其他的对于缓冲区的使用还需要对线程进行一下控制,如果用的不好,也会出现一个线程的空间需求超出了分配给他的大小的异常。一般情况用线程池可以缓解这样的情况。
原创粉丝点击