Android----图片缓存技术

一些大图片的处理,如果处理不当就会报OOM异常,除了加载图片时要用到缓存处理,还有一个比较重要的步骤要做,就是要先压缩图片。

1、压缩图片: 至于要压缩到什么状态就要看自己当时的处境了,压缩图片的时候既要达到一个小的值,又不能让其模糊 ,更不能拉伸图片。
   
   
* 加载内存卡图片
*/
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 设置了此属性一定要记得将值设置为false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
int be = (int) ((options.outHeight > options.outWidth ? options.outHeight / 150
: options.outWidth / 200));
if (be <= 0) // 判断200是否超过原始图片高度
be = 1; // 如果超过,则不进行缩放
options.inSampleSize = be;
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
System.gc();
Log.e(TAG, "OutOfMemoryError");
}

2、 软引用:
只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有
Strong Ref时才回收对象。
我们可以这样定义:map里面的键是用来放图片地址的,既可以是网络上的图片地址,也可以SDcard上的图片地址
map里面的值里面放的是持有软引用的Bitmap,当然如果你要放Drawable,那也是可以的。                                          
接下来就让我再介绍一下如何具体加载图片:
步骤:(1)先通过URL查看缓存中是否有图片,如果有,则直接去缓存中取得。 如果没有,就开线程重新去网上下载。
      (2)下载完了之后,就把图片放在缓存里面,方便下次可以直接从缓存中取得。
    
    
public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
SoftReference<Bitmap> reference = imageMap.get(imageUrl);
if(reference != null) {
if(reference.get() != null) {
return reference.get();
}
}
final Handler handler = new Handler() {
public void handleMessage(final android.os.Message msg) {
//加入到缓存中
Bitmap bitmap = (Bitmap)msg.obj;
imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
if(imageCallBack != null) {
imageCallBack.getBitmap(bitmap);
}
}
};
new Thread(){
public void run() {
Message message = handler.obtainMessage();
message.obj = downloadBitmap(imageUrl);
handler.sendMessage(message);
}
}.start();
return null ;
}
 
// 从网上下载图片
private Bitmap downloadBitmap (String imageUrl) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
return bitmap ;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
 
public interface ImageCallBack{
void getBitmap(Bitmap bitmap);
}

3、内存缓存技术
另外一种图片缓存的方式就是内存缓存技术。在Android中,有一个叫做LruCache类专门用来做图片缓存处理的。
它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉
步骤:
(1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/8,
           手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
(2)LruCache里面的键值对分别是URL和对应的图片
(3)重写了一个叫做sizeOf的方法,返回的是图片数量。
    
    
private LruCache<String, Bitmap> mMemoryCache;
private LruCacheUtils() {
if (mMemoryCache == null)
mMemoryCache = new LruCache<String, Bitmap>(
MAXMEMONRY / 8) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重写此方法来衡量每张图片的大小,默认返回图片数量。
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
 
@Override
protected void entryRemoved(boolean evicted, String key,
Bitmap oldValue, Bitmap newValue) {
Log.v("tag", "hard cache is full , push to soft cache");
}
};
}
(4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。
     移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。
     
     
public void clearCache() {
if (mMemoryCache != null) {
if (mMemoryCache.size() > 0) {
Log.d("CacheUtils",
"mMemoryCache.size() " + mMemoryCache.size());
mMemoryCache.evictAll();
Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
}
mMemoryCache = null;
}
}
 
public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (mMemoryCache.get(key) == null) {
if (key != null && bitmap != null)
mMemoryCache.put(key, bitmap);
} else
Log.w(TAG, "the res is aready exits");
}
 
public synchronized Bitmap getBitmapFromMemCache(String key) {
Bitmap bm = mMemoryCache.get(key);
if (key != null) {
return bm;
}
return null;
}
 
/**
* 移除缓存
*
* @param key
*/
public synchronized void removeImageCache(String key) {
if (key != null) {
if (mMemoryCache != null) {
Bitmap bm = mMemoryCache.remove(key);
if (bm != null)
bm.recycle();
}
}
}
两者的比
(1)因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,
     这让软引用和弱引用变得不再可靠。

(2)另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中, 因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃,所以我这里用得是LruCache来缓存图片,当存储Image的大小大于LruCache设定的值,系统自动释放内存,
这个类是3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包


4、LruCache的源码
    
    
1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package android.support.v4.util;
18
19 import java.util.LinkedHashMap;
20 import java.util.Map;
21
22 /**
23 * Static library version of {@link android.util.LruCache}. Used to write apps
24 * that run on API levels prior to 12. When running on API level 12 or above,
25 * this implementation is still used; it does not try to switch to the
26 * framework's implementation. See the framework SDK documentation for a class
27 * overview.
28 */
29 public class LruCache<K, V> {
30 private final LinkedHashMap<K, V> map;
31
32 /** Size of this cache in units. Not necessarily the number of elements. */
33 private int size; //当前cache的大小
34 private int maxSize; //cache最大大小
35
36 private int putCount; //put的次数
37 private int createCount; //create的次数
38 private int evictionCount; //回收的次数
39 private int hitCount; //命中的次数
40 private int missCount; //未命中次数
41
42 /**
43 * @param maxSize for caches that do not override {@link #sizeOf}, this is
44 * the maximum number of entries in the cache. For all other caches,
45 * this is the maximum sum of the sizes of the entries in this cache.
46 */
47 public LruCache(int maxSize) {
48 if (maxSize <= 0) {
49 throw new IllegalArgumentException("maxSize <= 0");
50 }
51 this.maxSize = maxSize;
52 //将LinkedHashMap的accessOrder设置为true来实现LRU
53 this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
54 }
55
56 /**
57 * Returns the value for {@code key} if it exists in the cache or can be
58 * created by {@code #create}. If a value was returned, it is moved to the
59 * head of the queue. This returns null if a value is not cached and cannot
60 * be created.
61 * 通过key获取相应的item,或者创建返回相应的item。相应的item会移动到队列的尾部,
62 * 如果item的value没有被cache或者不能被创建,则返回null。
63 */
64 public final V get(K key) {
65 if (key == null) {
66 throw new NullPointerException("key == null");
67 }
68
69 V mapValue;
70 synchronized (this) {
71 mapValue = map.get(key);
72 if (mapValue != null) {
73 //mapValue不为空表示命中,hitCount+1并返回mapValue对象
74 hitCount++;
75 return mapValue;
76 }
77 missCount++; //未命中
78 }
79
80 /*
81 * Attempt to create a value. This may take a long time, and the map
82 * may be different when create() returns. If a conflicting value was
83 * added to the map while create() was working, we leave that value in
84 * the map and release the created value.
85 * 如果未命中,则试图创建一个对象,这里create方法返回null,并没有实现创建对象的方法
86 * 如果需要事项创建对象的方法可以重写create方法。因为图片缓存时内存缓存没有命中会去
87 * 文件缓存中去取或者从网络下载,所以并不需要创建。
88 */
89 V createdValue = create(key);
90 if (createdValue == null) {
91 return null;
92 }
93 //假如创建了新的对象,则继续往下执行
94 synchronized (this) {
95 createCount++;
96 //将createdValue加入到map中,并且将原来键为key的对象保存到mapValue
97 mapValue = map.put(key, createdValue);
98 if (mapValue != null) {
99 // There was a conflict so undo that last put
100 //如果mapValue不为空,则撤销上一步的put操作。
101 map.put(key, mapValue);
102 } else {
103 //加入新创建的对象之后需要重新计算size大小
104 size += safeSizeOf(key, createdValue);
105 }
106 }
107
108 if (mapValue != null) {
109 entryRemoved(false, key, createdValue, mapValue);
110 return mapValue;
111 } else {
112 //每次新加入对象都需要调用trimToSize方法看是否需要回收
113 trimToSize(maxSize);
114 return createdValue;
115 }
116 }
117
118 /**
119 * Caches {@code value} for {@code key}. The value is moved to the head of
120 * the queue.
121 *
122 * @return the previous value mapped by {@code key}.
123 */
124 public final V put(K key, V value) {
125 if (key == null || value == null) {
126 throw new NullPointerException("key == null || value == null");
127 }
128
129 V previous;
130 synchronized (this) {
131 putCount++;
132 size += safeSizeOf(key, value); //size加上预put对象的大小
133 previous = map.put(key, value);
134 if (previous != null) {
135 //如果之前存在键为key的对象,则size应该减去原来对象的大小
136 size -= safeSizeOf(key, previous);
137 }
138 }
139
140 if (previous != null) {
141 entryRemoved(false, key, previous, value);
142 }
143 //每次新加入对象都需要调用trimToSize方法看是否需要回收
144 trimToSize(maxSize);
145 return previous;
146 }
147
148 /**
149 * @param maxSize the maximum size of the cache before returning. May be -1
150 * to evict even 0-sized elements.
151 * 此方法根据maxSize来调整内存cache的大小,如果maxSize传入-1,则清空缓存中的所有对象
152 */
153 private void trimToSize(int maxSize) {
154 while (true) {
155 K key;
156 V value;
157 synchronized (this) {
158 if (size < 0 || (map.isEmpty() && size != 0)) {
159 throw new IllegalStateException(getClass().getName()
160 + ".sizeOf() is reporting inconsistent results!");
161 }
162 //如果当前size小于maxSize或者map没有任何对象,则结束循环
163 if (size <= maxSize || map.isEmpty()) {
164 break;
165 }
166 //移除链表头部的元素,并进入下一次循环
167 Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
168 key = toEvict.getKey();
169 value = toEvict.getValue();
170 map.remove(key);
171 size -= safeSizeOf(key, value);
172 evictionCount++; //回收次数+1
173 }
174
175 entryRemoved(true, key, value, null);
176 }
177 }
178
179 /**
180 * Removes the entry for {@code key} if it exists.
181 *
182 * @return the previous value mapped by {@code key}.
183 * 从内存缓存中根据key值移除某个对象并返回该对象
184 */
185 public final V remove(K key) {
186 if (key == null) {
187 throw new NullPointerException("key == null");
188 }
189
190 V previous;
191 synchronized (this) {
192 previous = map.remove(key);
193 if (previous != null) {
194 size -= safeSizeOf(key, previous);
195 }
196 }
197
198 if (previous != null) {
199 entryRemoved(false, key, previous, null);
200 }
201
202 return previous;
203 }
204
205 /**
206 * Called for entries that have been evicted or removed. This method is
207 * invoked when a value is evicted to make space, removed by a call to
208 * {@link #remove}, or replaced by a call to {@link #put}. The default
209 * implementation does nothing.
210 *
211 * <p>The method is called without synchronization: other threads may
212 * access the cache while this method is executing.
213 *
214 * @param evicted true if the entry is being removed to make space, false
215 * if the removal was caused by a {@link #put} or {@link #remove}.
216 * @param newValue the new value for {@code key}, if it exists. If non-null,
217 * this removal was caused by a {@link #put}. Otherwise it was caused by
218 * an eviction or a {@link #remove}.
219 */
220 protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
221
222 /**
223 * Called after a cache miss to compute a value for the corresponding key.
224 * Returns the computed value or null if no value can be computed. The
225 * default implementation returns null.
226 *
227 * <p>The method is called without synchronization: other threads may
228 * access the cache while this method is executing.
229 *
230 * <p>If a value for {@code key} exists in the cache when this method
231 * returns, the created value will be released with {@link #entryRemoved}
232 * and discarded. This can occur when multiple threads request the same key
233 * at the same time (causing multiple values to be created), or when one
234 * thread calls {@link #put} while another is creating a value for the same
235 * key.
236 */
237 protected V create(K key) {
238 return null;
239 }
240
241 private int safeSizeOf(K key, V value) {
242 int result = sizeOf(key, value);
243 if (result < 0) {
244 throw new IllegalStateException("Negative size: " + key + "=" + value);
245 }
246 return result;
247 }
248
249 /**
250 * Returns the size of the entry for {@code key} and {@code value} in
251 * user-defined units. The default implementation returns 1 so that size
252 * is the number of entries and max size is the maximum number of entries.
253 *
254 * <p>An entry's size must not change while it is in the cache.
255 * 用来计算单个对象的大小,这里默认返回1,一般需要重写该方法来计算对象的大小
256 * xUtils中创建LruMemoryCache时就重写了sizeOf方法来计算bitmap的大小
257 * mMemoryCache = new LruMemoryCache<MemoryCacheKey, Bitmap>(globalConfig.getMemoryCacheSize()) {
258 * @Override
259 * protected int sizeOf(MemoryCacheKey key, Bitmap bitmap) {
260 * if (bitmap == null) return 0;
261 * return bitmap.getRowBytes() * bitmap.getHeight();
262 * }
263 * };
264 *
265 */
266 protected int sizeOf(K key, V value) {
267 return 1;
268 }
269
270 /**
271 * Clear the cache, calling {@link #entryRemoved} on each removed entry.
272 * 清空内存缓存
273 */
274 public final void evictAll() {
275 trimToSize(-1); // -1 will evict 0-sized elements
276 }
277
278 /**
279 * For caches that do not override {@link #sizeOf}, this returns the number
280 * of entries in the cache. For all other caches, this returns the sum of
281 * the sizes of the entries in this cache.
282 */
283 public synchronized final int size() {
284 return size;
285 }
286
287 /**
288 * For caches that do not override {@link #sizeOf}, this returns the maximum
289 * number of entries in the cache. For all other caches, this returns the
290 * maximum sum of the sizes of the entries in this cache.
291 */
292 public synchronized final int maxSize() {
293 return maxSize;
294 }
295
296 /**
297 * Returns the number of times {@link #get} returned a value.
298 */
299 public synchronized final int hitCount() {
300 return hitCount;
301 }
302
303 /**
304 * Returns the number of times {@link #get} returned null or required a new
305 * value to be created.
306 */
307 public synchronized final int missCount() {
308 return missCount;
309 }
310
311 /**
312 * Returns the number of times {@link #create(Object)} returned a value.
313 */
314 public synchronized final int createCount() {
315 return createCount;
316 }
317
318 /**
319 * Returns the number of times {@link #put} was called.
320 */
321 public synchronized final int putCount() {
322 return putCount;
323 }
324
325 /**
326 * Returns the number of values that have been evicted.
327 */
328 public synchronized final int evictionCount() {
329 return evictionCount;
330 }
331
332 /**
333 * Returns a copy of the current contents of the cache, ordered from least
334 * recently accessed to most recently accessed.
335 */
336 public synchronized final Map<K, V> snapshot() {
337 return new LinkedHashMap<K, V>(map);
338 }
339
340 @Override public synchronized final String toString() {
341 int accesses = hitCount + missCount;
342 int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
343 return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
344 maxSize, hitCount, missCount, hitPercent);
345 }
346 }





推荐博文: 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值