关闭

打造增强用户体验的图片缓存方案(包含OOM处理技术)

1022人阅读 评论(1) 收藏 举报
分类:

转自:http://blog.csdn.net/androidzhaoxiaogang/article/details/8152348


如果你还在因为大量不同size的图片缓存产生的OOM而烦恼,如果你还在因为用软引用(SoftReference)快速回收的蛋疼用户体验而不知所措,那么我建议无论你是高手还是菜鸟,真的很有必要看一下这篇文章,希望能从中给你一些启发,给你的产品用户带去一些好的体验。

思维的火花

既然我们要提供用户的体验,既然我们摒弃了软应用,那么我这里才用的是使用LRU的缓存机制来达到我们的目的。在android 3.1以上我们可以使用LruCache类,但如果在低一些的版本我们则只要把源代码copy出来放进工程就ok了。但是,仅仅把LruCache的代码copy出来只是完成了我们实现这里图片缓存方案的准备工作。

精心的构建

1.LruCache
[java] view plaincopy
  1. package XXXl;  
  2.   
  3. import java.util.LinkedHashMap;  
  4. import java.util.Map;  
  5.   
  6. /** 
  7.  * A cache that holds strong references to a limited number of values. Each time 
  8.  * a value is accessed, it is moved to the head of a queue. When a value is 
  9.  * added to a full cache, the value at the end of that queue is evicted and may 
  10.  * become eligible for garbage collection. 
  11.  * 
  12.  * <p>If your cached values hold resources that need to be explicitly released, 
  13.  * override {@link #entryRemoved}. 
  14.  * 
  15.  * <p>If a cache miss should be computed on demand for the corresponding keys, 
  16.  * override {@link #create}. This simplifies the calling code, allowing it to 
  17.  * assume a value will always be returned, even when there's a cache miss. 
  18.  * 
  19.  * <p>By default, the cache size is measured in the number of entries. Override 
  20.  * {@link #sizeOf} to size the cache in different units. For example, this cache 
  21.  * is limited to 4MiB of bitmaps: 
  22.  * <pre>   {@code 
  23.  *   int cacheSize = 4 * 1024 * 1024; // 4MiB 
  24.  *   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { 
  25.  *       protected int sizeOf(String key, Bitmap value) { 
  26.  *           return value.getByteCount(); 
  27.  *       } 
  28.  *   }}</pre> 
  29.  * 
  30.  * <p>This class is thread-safe. Perform multiple cache operations atomically by 
  31.  * synchronizing on the cache: <pre>   {@code 
  32.  *   synchronized (cache) { 
  33.  *     if (cache.get(key) == null) { 
  34.  *         cache.put(key, value); 
  35.  *     } 
  36.  *   }}</pre> 
  37.  * 
  38.  * <p>This class does not allow null to be used as a key or value. A return 
  39.  * value of null from {@link #get}, {@link #put} or {@link #remove} is 
  40.  * unambiguous: the key was not in the cache. 
  41.  */  
  42. /** 
  43.  * Static library version of {@code android.util.LruCache}. Used to write apps 
  44.  * that run on API levels prior to 12. When running on API level 12 or above, 
  45.  * this implementation is still used; it does not try to switch to the 
  46.  * framework's implementation. See the framework SDK documentation for a class 
  47.  * overview. 
  48.  */  
  49. public class LruCache<K, V> {  
  50.     private LogUtils mLog = LogUtils.getLog(LruCache.class);  
  51.     private final LinkedHashMap<K, V> map;  
  52.   
  53.     /** Size of this cache in units. Not necessarily the number of elements. */  
  54.     private int size;  
  55.     private int maxSize;  
  56.   
  57.     private int putCount;  
  58.     private int createCount;  
  59.     private int evictionCount;  
  60.     private int hitCount;  
  61.     private int missCount;  
  62.   
  63.     /** 
  64.      * @param maxSize for caches that do not override {@link #sizeOf}, this is 
  65.      *     the maximum number of entries in the cache. For all other caches, 
  66.      *     this is the maximum sum of the sizes of the entries in this cache. 
  67.      */  
  68.     public LruCache(int maxSize) {  
  69.         if (maxSize <= 0) {  
  70.             throw new IllegalArgumentException("maxSize <= 0");  
  71.         }  
  72.         this.maxSize = maxSize;  
  73.         this.map = new LinkedHashMap<K, V>(00.75f, true);  
  74.     }  
  75.   
  76.     /** 
  77.      * Returns the value for {@code key} if it exists in the cache or can be 
  78.      * created by {@code #create}. If a value was returned, it is moved to the 
  79.      * head of the queue. This returns null if a value is not cached and cannot 
  80.      * be created. 
  81.      */  
  82.     public final V get(K key) {  
  83.         if (key == null) {  
  84.             throw new NullPointerException("key == null");  
  85.         }  
  86.   
  87.         V mapValue;  
  88.         synchronized (this) {  
  89.             mapValue = map.get(key);  
  90.             if (mapValue != null) {  
  91.                 hitCount++;  
  92.                 return mapValue;  
  93.             }  
  94.             missCount++;  
  95.         }  
  96.   
  97.         /* 
  98.          * Attempt to create a value. This may take a long time, and the map 
  99.          * may be different when create() returns. If a conflicting value was 
  100.          * added to the map while create() was working, we leave that value in 
  101.          * the map and release the created value. 
  102.          */  
  103.   
  104.         V createdValue = create(key);  
  105.         if (createdValue == null) {  
  106.             return null;  
  107.         }  
  108.   
  109.         synchronized (this) {  
  110.             createCount++;  
  111.             mapValue = map.put(key, createdValue);  
  112.   
  113.             if (mapValue != null) {  
  114.                 // There was a conflict so undo that last put  
  115.                 map.put(key, mapValue);  
  116.             } else {  
  117.                 size += safeSizeOf(key, createdValue);  
  118.             }  
  119.         }  
  120.   
  121.         if (mapValue != null) {  
  122.             entryRemoved(false, key, createdValue, mapValue);  
  123.             return mapValue;  
  124.         } else {  
  125.             trimToSize(maxSize);  
  126.             return createdValue;  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * Caches {@code value} for {@code key}. The value is moved to the head of 
  132.      * the queue. 
  133.      * 
  134.      * @return the previous value mapped by {@code key}. 
  135.      */  
  136.     public final V put(K key, V value) {  
  137.         if (key == null || value == null) {  
  138.             throw new NullPointerException("key == null || value == null");  
  139.         }  
  140.   
  141.         V previous;  
  142.         synchronized (this) {  
  143.             putCount++;  
  144.             size += safeSizeOf(key, value);  
  145.             previous = map.put(key, value);  
  146.             if (previous != null) {  
  147.                 size -= safeSizeOf(key, previous);  
  148.             }  
  149.         }  
  150.   
  151.         if (previous != null) {  
  152.             entryRemoved(false, key, previous, value);  
  153.         }  
  154.   
  155.         mLog.debug("maxSize    :" + maxSize);  
  156.         mLog.debug("total size :" + size);  
  157.   
  158.         trimToSize(maxSize);  
  159.         return previous;  
  160.     }  
  161.   
  162.     /** 
  163.      * @param maxSize the maximum size of the cache before returning. May be -1 
  164.      *     to evict even 0-sized elements. 
  165.      */  
  166.     public void trimToSize(int maxSize) {  
  167.         while (true) {  
  168.             K key;  
  169.             V value;  
  170.             synchronized (this) {  
  171.                 if (size < 0 || (map.isEmpty() && size != 0)) {  
  172.                     throw new IllegalStateException(getClass().getName()  
  173.                             + ".sizeOf() is reporting inconsistent results!");  
  174.                 }  
  175.   
  176.                 if (size <= maxSize || map.isEmpty()) {  
  177.                     break;  
  178.                 }  
  179.   
  180.                 Map.Entry<K, V> toEvict = map.entrySet().iterator().next();  
  181.                 key = toEvict.getKey();  
  182.                 value = toEvict.getValue();  
  183.                 map.remove(key);  
  184.                 size -= safeSizeOf(key, value);  
  185.                 evictionCount++;  
  186.             }  
  187.   
  188.             entryRemoved(true, key, value, null);  
  189.         }  
  190.     }  
  191.   
  192.     /** 
  193.      * Removes the entry for {@code key} if it exists. 
  194.      * 
  195.      * @return the previous value mapped by {@code key}. 
  196.      */  
  197.     public final V remove(K key) {  
  198.         if (key == null) {  
  199.             throw new NullPointerException("key == null");  
  200.         }  
  201.   
  202.         V previous;  
  203.         synchronized (this) {  
  204.             previous = map.remove(key);  
  205.             if (previous != null) {  
  206.                 size -= safeSizeOf(key, previous);  
  207.             }  
  208.         }  
  209.   
  210.         if (previous != null) {  
  211.             entryRemoved(false, key, previous, null);  
  212.         }  
  213.   
  214.         return previous;  
  215.     }  
  216.   
  217.     /** 
  218.      * Called for entries that have been evicted or removed. This method is 
  219.      * invoked when a value is evicted to make space, removed by a call to 
  220.      * {@link #remove}, or replaced by a call to {@link #put}. The default 
  221.      * implementation does nothing. 
  222.      * 
  223.      * <p>The method is called without synchronization: other threads may 
  224.      * access the cache while this method is executing. 
  225.      * 
  226.      * @param evicted true if the entry is being removed to make space, false 
  227.      *     if the removal was caused by a {@link #put} or {@link #remove}. 
  228.      * @param newValue the new value for {@code key}, if it exists. If non-null, 
  229.      *     this removal was caused by a {@link #put}. Otherwise it was caused by 
  230.      *     an eviction or a {@link #remove}. 
  231.      */  
  232.     protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}  
  233.   
  234.     /** 
  235.      * Called after a cache miss to compute a value for the corresponding key. 
  236.      * Returns the computed value or null if no value can be computed. The 
  237.      * default implementation returns null. 
  238.      * 
  239.      * <p>The method is called without synchronization: other threads may 
  240.      * access the cache while this method is executing. 
  241.      * 
  242.      * <p>If a value for {@code key} exists in the cache when this method 
  243.      * returns, the created value will be released with {@link #entryRemoved} 
  244.      * and discarded. This can occur when multiple threads request the same key 
  245.      * at the same time (causing multiple values to be created), or when one 
  246.      * thread calls {@link #put} while another is creating a value for the same 
  247.      * key. 
  248.      */  
  249.     protected V create(K key) {  
  250.         return null;  
  251.     }  
  252.   
  253.     private int safeSizeOf(K key, V value) {  
  254.         int result = sizeOf(key, value);  
  255.         if (result < 0) {  
  256.             throw new IllegalStateException("Negative size: " + key + "=" + value);  
  257.         }  
  258.         mLog.debug("size :" + result);  
  259.   
  260.         return result;  
  261.     }  
  262.   
  263.     /** 
  264.      * Returns the size of the entry for {@code key} and {@code value} in 
  265.      * user-defined units.  The default implementation returns 1 so that size 
  266.      * is the number of entries and max size is the maximum number of entries. 
  267.      * 
  268.      * <p>An entry's size must not change while it is in the cache. 
  269.      */  
  270.     protected int sizeOf(K key, V value) {  
  271.         return 1;  
  272.     }  
  273.   
  274.     /** 
  275.      * Clear the cache, calling {@link #entryRemoved} on each removed entry. 
  276.      */  
  277.     public final void evictAll() {  
  278.         trimToSize(-1); // -1 will evict 0-sized elements  
  279.     }  
  280.   
  281.     /** 
  282.      * For caches that do not override {@link #sizeOf}, this returns the number 
  283.      * of entries in the cache. For all other caches, this returns the sum of 
  284.      * the sizes of the entries in this cache. 
  285.      */  
  286.     public synchronized final int size() {  
  287.         return size;  
  288.     }  
  289.   
  290.     /** 
  291.      * For caches that do not override {@link #sizeOf}, this returns the maximum 
  292.      * number of entries in the cache. For all other caches, this returns the 
  293.      * maximum sum of the sizes of the entries in this cache. 
  294.      */  
  295.     public synchronized final int maxSize() {  
  296.         return maxSize;  
  297.     }  
  298.   
  299.     /** 
  300.      * Returns the number of times {@link #get} returned a value. 
  301.      */  
  302.     public synchronized final int hitCount() {  
  303.         return hitCount;  
  304.     }  
  305.   
  306.     /** 
  307.      * Returns the number of times {@link #get} returned null or required a new 
  308.      * value to be created. 
  309.      */  
  310.     public synchronized final int missCount() {  
  311.         return missCount;  
  312.     }  
  313.   
  314.     /** 
  315.      * Returns the number of times {@link #create(Object)} returned a value. 
  316.      */  
  317.     public synchronized final int createCount() {  
  318.         return createCount;  
  319.     }  
  320.   
  321.     /** 
  322.      * Returns the number of times {@link #put} was called. 
  323.      */  
  324.     public synchronized final int putCount() {  
  325.         return putCount;  
  326.     }  
  327.   
  328.     /** 
  329.      * Returns the number of values that have been evicted. 
  330.      */  
  331.     public synchronized final int evictionCount() {  
  332.         return evictionCount;  
  333.     }  
  334.   
  335.     /** 
  336.      * Returns a copy of the current contents of the cache, ordered from least 
  337.      * recently accessed to most recently accessed. 
  338.      */  
  339.     public synchronized final Map<K, V> snapshot() {  
  340.         return new LinkedHashMap<K, V>(map);  
  341.     }  
  342.   
  343.     @Override public synchronized final String toString() {  
  344.         int accesses = hitCount + missCount;  
  345.         int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;  
  346.         return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",  
  347.                 maxSize, hitCount, missCount, hitPercent);  
  348.     }  
  349. }  

2.自定义ImageView

[java] view plaincopy
  1. package XXX.view;  
  2.   
  3. import java.io.FilterInputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.lang.ref.SoftReference;  
  7. import java.util.HashMap;  
  8. import java.util.Map;  
  9. import java.util.WeakHashMap;  
  10. import java.util.concurrent.RejectedExecutionException;  
  11.   
  12. import android.app.ActivityManager;  
  13. import android.content.Context;  
  14. import android.content.pm.PackageInfo;  
  15. import android.content.pm.PackageManager;  
  16. import android.content.pm.PackageManager.NameNotFoundException;  
  17. import android.graphics.Bitmap;  
  18. import android.graphics.BitmapFactory;  
  19. import android.graphics.BitmapFactory.Options;  
  20. import android.graphics.Canvas;  
  21. import android.graphics.drawable.BitmapDrawable;  
  22. import android.graphics.drawable.Drawable;  
  23. import android.os.AsyncTask;  
  24. import android.text.TextUtils;  
  25. import android.util.AttributeSet;  
  26. import android.widget.ImageView;  
  27. import ch.boye.httpclientandroidlib.HttpEntity;  
  28. import ch.boye.httpclientandroidlib.HttpResponse;  
  29. import ch.boye.httpclientandroidlib.HttpStatus;  
  30. import ch.boye.httpclientandroidlib.client.methods.HttpGet;  
  31.   
  32. public class CacheImageView extends ImageView {  
  33.     private static int mCacheSize;  
  34.   
  35.     private int mDefaultImage = 0;  
  36.   
  37.     private static Map<ImageView, String> mImageViews;  
  38.   
  39.     private static LruCache<String, Bitmap> mLruCache;  
  40.   
  41.     private static HashMap<Integer, SoftReference<Bitmap>> mResImage;  
  42.   
  43.     private Context mContext;  
  44.   
  45.     public CacheImageView (Context context, AttributeSet attrs, int defStyle) {  
  46.         super(context, attrs, defStyle);  
  47.         init(context);  
  48.     }  
  49.   
  50.     public CacheImageView (Context context, AttributeSet attrs) {  
  51.         super(context, attrs);  
  52.         init(context);  
  53.   
  54.     }  
  55.   
  56.     public CacheImageView (Context context) {  
  57.         super(context);  
  58.         init(context);  
  59.     }  
  60.   
  61.     private void init(Context context) {  
  62.         if (mImageViews == null) {  
  63.             mImageViews = new WeakHashMap<ImageView, String>();  
  64.         }  
  65.   
  66.         if (mLruCache == null) {  
  67.             final int memClass = ((ActivityManager)context  
  68.                     .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();  
  69.   
  70.             // Use 1/8th of the available memory for this memory cache.  
  71.             mCacheSize = 1024 * 1024 * memClass / 8;  
  72.             mLruCache = new LruCache<String, Bitmap>(mCacheSize) {  
  73.                 @Override  
  74.                 protected int sizeOf(String key, Bitmap bitmap) {  
  75.                     // The cache size will be measured in bytes rather than  
  76.                     // number of items.  
  77.                     return bitmap.getRowBytes() * bitmap.getHeight();  
  78.                 }  
  79.   
  80.                 @Override  
  81.                 protected void entryRemoved(boolean evicted, String key, Bitmap oldValue,  
  82.                         Bitmap newValue) {  
  83.                     if (evicted && oldValue !=null && !oldValue.isRecycled()) {  
  84.                         oldValue.recycle();  
  85.                         oldValue = null;  
  86.                     }  
  87.                 }  
  88.             };  
  89.         }  
  90.   
  91.        if (mResImage == null) {  
  92.             mResImage = new HashMap<Integer, SoftReference<Bitmap>>();  
  93.         }  
  94.   
  95.        mContext  = context;  
  96.     }  
  97.   
  98.     @Override  
  99.     protected void onDraw(Canvas canvas) {  
  100.         BitmapDrawable drawable = (BitmapDrawable)getDrawable();  
  101.         if (drawable == null ){  
  102.             setImageBitmap(getLoadingBitmap(mContext));  
  103.         } else {  
  104.             if( drawable.getBitmap() == null || drawable.getBitmap().isRecycled()) {  
  105.                 setImageBitmap(getLoadingBitmap(mContext));  
  106.             }  
  107.         }  
  108.         super.onDraw(canvas);  
  109.     }  
  110.   
  111.     public void setImageUrl(String url, int resId) {  
  112.         mDefaultImage = resId;  
  113.         mImageViews.put(this, url);  
  114.         Bitmap bitmap = getBitmapFromCache(url);  
  115.         if (bitmap == null || bitmap.isRecycled()) {  
  116.             setImageBitmap(getLoadingBitmap(mContext));  
  117.             try {  
  118.                 new DownloadTask().execute(url);  
  119.             } catch (RejectedExecutionException e) {  
  120.                 // do nothing, just keep not crash  
  121.             }  
  122.         } else {  
  123.             setImageBitmap(bitmap);  
  124.         }  
  125.     }  
  126.   
  127.     private Bitmap getLoadingBitmap(Context context) {  
  128.         SoftReference<Bitmap> loading = mResImage.get(mDefaultImage);  
  129.         if (loading == null || loading.get() == null || loading.get().isRecycled()) {  
  130.             loading = new SoftReference<Bitmap>(BitmapFactory.decodeResource(  
  131.                     context.getResources(), mDefaultImage));  
  132.             mResImage.put(mDefaultImage, loading);  
  133.         }  
  134.         return loading.get();  
  135.     }  
  136.   
  137.     private class DownloadTask extends AsyncTask<String, Void, Bitmap> {  
  138.         private String mParams;  
  139.   
  140.         @Override  
  141.         public Bitmap doInBackground(String... params) {  
  142.             mParams = params[0];  
  143.             Bitmap bm = null;  
  144.             if (mParams.startsWith("http:") || mParams.startsWith("https:")) {// 网络列表icon  
  145.                 bm = download(mParams);  
  146.             } else {  
  147.                 // other types of icons  
  148.             }  
  149.             addBitmapToCache(mParams, bm);  
  150.             return bm;  
  151.         }  
  152.   
  153.         @Override  
  154.         public void onPostExecute(Bitmap bitmap) {  
  155.             String tag = mImageViews.get(RemoteImageView.this);  
  156.             if (!TextUtils.isEmpty(tag) && tag.equals(mParams)) {  
  157.                 if (bitmap != null) {  
  158.                     setImageBitmap(bitmap);  
  159.                 }  
  160.             }  
  161.         }  
  162.     };  
  163.   
  164.     /* 
  165.      * An InputStream that skips the exact number of bytes provided, unless it 
  166.      * reaches EOF. 
  167.      */  
  168.     static class FlushedInputStream extends FilterInputStream {  
  169.         public FlushedInputStream(InputStream inputStream) {  
  170.             super(inputStream);  
  171.         }  
  172.   
  173.         @Override  
  174.         public long skip(long n) throws IOException {  
  175.             long totalBytesSkipped = 0L;  
  176.             while (totalBytesSkipped < n) {  
  177.                 long bytesSkipped = in.skip(n - totalBytesSkipped);  
  178.                 if (bytesSkipped == 0L) {  
  179.                     int b = read();  
  180.                     if (b < 0) {  
  181.                         break// we reached EOF  
  182.                     } else {  
  183.                         bytesSkipped = 1// we read one byte  
  184.                     }  
  185.                 }  
  186.                 totalBytesSkipped += bytesSkipped;  
  187.             }  
  188.             return totalBytesSkipped;  
  189.         }  
  190.     }  
  191.   
  192.     private Bitmap download(String url) {  
  193.         InputStream in = null;  
  194.         HttpEntity entity = null;  
  195.         Bitmap bmp = null;  
  196.         try {  
  197.             final HttpGet get = new HttpGet(url);  
  198.             final HttpResponse response = HttpManager.execute(mContext, get);  
  199.             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
  200.                 entity = response.getEntity();  
  201.                 in = entity.getContent();  
  202.                 try {  
  203.                     bmp = getDecodeBitmap(in, url);  
  204.                 } catch (OutOfMemoryError err) {  
  205.                     Runtime.getRuntime().gc();  
  206.                     bmp = getDecodeBitmap(in, url);  
  207.                 }  
  208.             } else {  
  209.                 get.abort();  
  210.                 return bmp;  
  211.             }  
  212.         } catch (Exception e) {  
  213.             return bmp;  
  214.         } finally {  
  215.             IOUtils.closeStream(in);  
  216.         }  
  217.         return bmp;  
  218.     }  
  219.   
  220.     private Bitmap getDecodeBitmap(InputStream in, String url) {  
  221.         Options options = new Options();  
  222.         options.inPurgeable = true;  
  223.         options.inInputShareable = true;  
  224.         return BitmapFactory.decodeStream(new FlushedInputStream(in), null, options);  
  225.     }  
  226.   
  227.     public void addBitmapToCache(String url, Bitmap bitmap) {  
  228.         if (bitmap != null) {  
  229.             mLruCache.put(url, bitmap);  
  230.             Runtime.getRuntime().gc();  
  231.         }  
  232.     }  
  233.   
  234.     /** 
  235.      * @param url The URL of the image that will be retrieved from the cache. 
  236.      * @return The cached bitmap or null if it was not found. 
  237.      */  
  238.     public static Bitmap getBitmapFromCache(String url) {  
  239.         return mLruCache.get(url);  
  240.     }  
  241.   
  242.     public static void recycle() {  
  243.         if (mImageViews != null && !mImageViews.isEmpty()) {  
  244.             mImageViews.clear();  
  245.             mImageViews = null;  
  246.         }  
  247.         if (mLruCache != null) {  
  248.             mLruCache.evictAll();  
  249.             mLruCache = null;  
  250.         }  
  251.         if (mResImage != null) {  
  252.             for (SoftReference<Bitmap> reference : mResImage.values()) {  
  253.                 Bitmap bitmap = reference.get();  
  254.                 if (bitmap != null && !bitmap.isRecycled()) {  
  255.                     bitmap.recycle();  
  256.                     bitmap = null;  
  257.                 }  
  258.             }  
  259.             mResImage = null;  
  260.         }  
  261.     }  
  262. }  



这一步是实现LRU缓存方案的最关键一步,里面需要对几个地方做详细和认真的解释。

在初始化LruCache的时候我们有用到:
[java] view plaincopy
  1. protected void entryRemoved(boolean evicted, String key, Bitmap oldValue,  
  2.                         Bitmap newValue) {  
  3.       if (evicted && oldValue !=null && !oldValue.isRecycled()) {  
  4.           oldValue.recycle();  
  5.           oldValue = null;  
  6.       }  
  7.  }  


注意if里面的条件一共由3个(evicted && oldValue !=null && !oldValue.isRecycled())组成一个都不能少,至于原因希望你们去思考。

另外调用addBitmapToCache方法我是在后台调用的,没有在主线程里面操作,原因是里面调用了Runtime.getRuntime().gc(),基本上每次GC的执行都要花去20~50ms如果是在列表里面的话,对ui应该有一定的影响。在此强调一下Runtime.getRuntime().gc()在每次加载图片之后最好调用他。这是一个小兄弟测试的结果,在android调用GC有助于虚拟机减少内存碎片和加速内存碎片的重整理。

下载图片建立连接我用了httpclient的连接池方式,如果你觉的麻烦你可以使用URLconnection,这里暂时不给出httpclient连接池框架的部分,如果你随时关注我的话,你可以从我后面的博客中看到关于它的话题。

3.如何使用

可能你的项目中有多个地方要用到图片,那么只要在你的xml中需要用到imageview的这样去定义(以listview的row举例):

[html] view plaincopy
  1. <XXX.view.CacheImageView  
  2.         android:id="@+id/icon"  
  3.         android:layout_width="40dip"  
  4.         android:layout_height="40dip"  
  5.         android:layout_marginLeft="10dip" />  

然后再你的adapter代码中只需要简单的两句:
[java] view plaincopy
  1. holder.icon = (CacheImageView)convertView.findViewById(R.id.icon);  
  2. holder.icon.setImageUrl(url, resId);  

完美的总结

该方案是尽量减少图片被回收的时间,但是并不是不被回收,所以需要一直展示给用户的情况不适合本方案。

对于某些国产机内存特小的那种,即使使用软引用都很容易挂的那种,建议不要再设置为内存的8分之一大小,而是获取到手机的UA(model),去硬编码一个大小吧。

本方案在这里只展示了基于内存的缓存方式,基于disk的部分代码,朋友们可以去实现,这里不再赘述。

可能本方案还有很多不足,欢迎大家提意见,我好不断完善
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:746065次
    • 积分:9218
    • 等级:
    • 排名:第1987名
    • 原创:111篇
    • 转载:376篇
    • 译文:0篇
    • 评论:111条
    文章分类
    最新评论