Skip to content

Commit

Permalink
解决图片缓存的多线程并发导致下载失败的bug
Browse files Browse the repository at this point in the history
  • Loading branch information
zhaoqp2010 committed Nov 18, 2013
1 parent e88840b commit c2150b6
Show file tree
Hide file tree
Showing 24 changed files with 422 additions and 197 deletions.
Binary file modified AndBase/bin/andbase.jar
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageCache$1.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageCache.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageDownloadPool$2.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageDownloadPool.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageDownloadQueue.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageDownloadTask.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/bitmap/AbImageDownloader.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/db/orm/dao/AbDBDaoImpl.class
Binary file not shown.
Binary file modified AndBase/bin/classes/com/ab/global/AbAppData.class
Binary file not shown.
3 changes: 0 additions & 3 deletions AndBase/bin/jarlist.cache

This file was deleted.

180 changes: 167 additions & 13 deletions AndBase/src/com/ab/bitmap/AbImageCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@
*/
package com.ab.bitmap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.Log;

import com.ab.util.AbImageUtil;
import com.ab.global.AbAppData;
import com.ab.util.AbMd5;
import com.ab.util.AbStrUtil;

Expand All @@ -33,45 +39,91 @@

public class AbImageCache {

/** The tag. */
private static String TAG = "AbImageCache";

/** The Constant D. */
private static final boolean D = AbAppData.DEBUG;

/** 4MB. */
public static int cacheSize = 10 * 1024 * 1024;

/** 为了加快速度,在内存中开启缓存,最新的LruCache. */
private static LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
private static final LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}};

/**正在下载中的线程*/
private static final HashMap<String, Runnable> runRunnableCache = new HashMap<String, Runnable>();

/**等待中的线程*/
private static final List<HashMap<String, Runnable>> waitRunnableList = new ArrayList<HashMap<String, Runnable>>();

/**锁对象*/
public static final ReentrantLock lock = new ReentrantLock();

/**
* 描述:从缓存中获取这个Bitmap.
*
* @param key the key
* @return the bitmap from mem cache
*/
public static Bitmap getBitmapFromMemCache(String key) {
return (Bitmap)bitmapCache.get(key);
public static Bitmap getBitmapFromCache(String key) {
return bitmapCache.get(key);
}

/**
* 描述:增加一个图片到缓存.
*
* @param key 一般为一个网络文件的url
* @param key 通过url计算的缓存key
* @param bitmap the bitmap
*/
public static void addBitmapToMemoryCache(String key,Bitmap bitmap){
if(AbStrUtil.isEmpty(key) || bitmap == null){
return;
public static void addBitmapToCache(String key,Bitmap bitmap){
try {
if(D) Log.d(TAG, "图片下载完成:"+key);
lock.lock();
if(AbStrUtil.isEmpty(key) || bitmap == null){
return;
}
if (getBitmapFromCache(key) == null) {
bitmapCache.put(key, bitmap);
if(D) Log.d(TAG, "存入缓存:"+key+","+bitmap);
}
//表示下载中的缓存清除
removeRunRunnableFromCache(key);
if(D) Log.d(TAG, "检查挂起线程:"+waitRunnableList.size());
//唤醒等待线程并移除列表
removeWaitRunnableFromCache(key);
} catch (Exception e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}

/**
* 描述:从缓存删除.
*
* @param key 通过url计算的缓存key
*/
public static void removeBitmapFromCache(String key){
try {
lock.lock();
if (getBitmapFromCache(key) != null) {
bitmapCache.remove(key);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
lock.unlock();
}
if (getBitmapFromMemCache(key) == null) {
bitmapCache.put(key, bitmap);
}
}

/**
* 描述:清空缓存的Bitmap.
*/
public static void removeAllBitmapFromCache() {
public static void removeAllBitmapFromCache() {
bitmapCache.evictAll();
}

Expand All @@ -82,9 +134,111 @@ public static void removeAllBitmapFromCache() {
* @param height 图片高度.
* @param type 处理类型.
*/
public static String getCacheKey(String url, int width, int height,int type) {
public static String getCacheKey(String url, int width, int height,int type) {
return AbMd5.MD5(new StringBuilder(url.length() + 12).append("#W").append(width)
.append("#H").append(height).append("#T").append(type).append(url).toString());
}

/**
* 描述:从缓存中获取这个正在执行线程.
*
* @param key the key
* @return the runnable
*/
public static Runnable getRunRunnableFromCache(String key) {
return runRunnableCache.get(key);
}

/**
* 描述:增加一个正在执行线程的记录.
*
* @param key 通过url计算的缓存key
* @param runnable the runnable
*/
public static void addToRunRunnableCache(String key,Runnable runnable){
try {
lock.lock();
if(AbStrUtil.isEmpty(key) || runnable == null){
return;
}
if (getRunRunnableFromCache(key) == null) {
runRunnableCache.put(key, runnable);
}
}catch (Exception e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}

/**
* 描述:从缓存一个正在执行的线程.
*
* @param key 通过url计算的缓存key
*/
public static void removeRunRunnableFromCache(String key){
if (getRunRunnableFromCache(key) != null) {
runRunnableCache.remove(key);
}
}

/**
* 描述:从缓存中获取这个正在等待线程.
*
* @param key the key
* @return the runnable
*/
public static Runnable getWaitRunnableFromCache(String key) {
return runRunnableCache.get(key);
}

/**
* 描述:增加一个等待线程的记录.
*
* @param key 通过url计算的缓存key
* @param runnable the runnable
*/
public static void addToWaitRunnableCache(String key,Runnable runnable){
try {
lock.lock();
if(AbStrUtil.isEmpty(key) || runnable == null){
return;
}
HashMap<String, Runnable> runnableMap = new HashMap<String, Runnable>();
runnableMap.put(key, runnable);
waitRunnableList.add(runnableMap);
} catch (Exception e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}

/**
* 描述:从缓存删除一个等待线程.
*
* @param key 通过url计算的缓存key
*/
public static void removeWaitRunnableFromCache(String key){
try {
lock.lock();
for(int i=0;i<waitRunnableList.size();i++){
HashMap<String, Runnable> runnableMap = waitRunnableList.get(i);
Runnable runnable = runnableMap.get(key);
if (runnable != null) {
if(D) Log.d(TAG, "从缓存删除并唤醒:"+runnable);
synchronized(runnable){
runnable.notify();
}
waitRunnableList.remove(runnableMap);
i--;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}

}
46 changes: 32 additions & 14 deletions AndBase/src/com/ab/bitmap/AbImageDownloadPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import android.os.Handler;
import android.os.Message;
Expand All @@ -26,7 +27,6 @@
import com.ab.global.AbAppData;
import com.ab.util.AbAppUtil;
import com.ab.util.AbFileUtil;
import com.ab.util.AbMd5;
import com.ab.util.AbStrUtil;
// TODO: Auto-generated Javadoc
/**
Expand Down Expand Up @@ -94,28 +94,45 @@ public static AbImageDownloadPool getInstance() {
* @param item the item
*/
public void download(final AbImageDownloadItem item) {
String urlImage = item.imageUrl;
if(AbStrUtil.isEmpty(urlImage)){
String imageUrl = item.imageUrl;
if(AbStrUtil.isEmpty(imageUrl)){
if(D)Log.d(TAG, "图片URL为空,请先判断");
}else{
urlImage = urlImage.trim();
imageUrl = imageUrl.trim();
}
final String url = urlImage;
// 如果缓存过就从缓存中取出数据
String cacheKey = AbImageCache.getCacheKey(url, item.width, item.height, item.type);
item.bitmap = AbImageCache.getBitmapFromMemCache(cacheKey);
if(D) Log.d(TAG, "缓存中获取的"+cacheKey+":"+item.bitmap);
//从缓存中获取图片
final String cacheKey = AbImageCache.getCacheKey(imageUrl, item.width, item.height, item.type);
item.bitmap = AbImageCache.getBitmapFromCache(cacheKey);

if(item.bitmap == null){
// 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
// 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
executorService.submit(new Runnable() {
public void run() {
try {
item.bitmap = AbFileUtil.getBitmapFromSDCache(item.imageUrl,item.type,item.width,item.height);
if(D) Log.d(TAG, "下载从SD卡得到的:"+item.bitmap);
String cacheKey = AbImageCache.getCacheKey(url, item.width, item.height, item.type);
AbImageCache.addBitmapToMemoryCache(cacheKey,item.bitmap);
//逻辑:判断这个任务是否有其他线程再执行,如果有等待,直到下载完成唤醒显示
Runnable runnable = AbImageCache.getRunRunnableFromCache(cacheKey);
if(runnable != null){

//线程等待通知后显示
if(D) Log.d(TAG, "等待:"+cacheKey+","+item.imageUrl);
AbImageCache.addToWaitRunnableCache(cacheKey, this);
synchronized(this){
this.wait();
}
if(D) Log.d(TAG, "我醒了:"+item.imageUrl);
//直接获取
item.bitmap = AbImageCache.getBitmapFromCache(cacheKey);
}else{
//增加下载中的线程记录
if(D) Log.d(TAG, "增加图片下载中:"+cacheKey+","+item.imageUrl);
AbImageCache.addToRunRunnableCache(cacheKey, this);
item.bitmap = AbFileUtil.getBitmapFromSDCache(item.imageUrl,item.type,item.width,item.height);
//增加到下载完成的缓存,删除下载中的记录和等待的记录,同时唤醒所有等待列表中key与其key相同的线程
AbImageCache.addBitmapToCache(cacheKey,item.bitmap);
}

} catch (Exception e) {
if(D) Log.d(TAG, "error:"+item.imageUrl);
e.printStackTrace();
} finally{
if (item.listener != null) {
Expand All @@ -126,6 +143,7 @@ public void run() {
}
}
});

}else{
if (item.listener != null) {
Message msg = handler.obtainMessage();
Expand Down
Loading

0 comments on commit c2150b6

Please sign in to comment.