话说接触自定义线程池的时候还是几年前用Volley框架的时候,当时就注意到了一个东西叫队列,后来在实际开发中也没有用到相关的东西,只是写过简单的demo而已,现在各种框架很成熟,用多了就缺少了动手能力,所以我就简单了实现了这个框架。
整成思路是:默认创建了5个线程,通过LinkedBlockingQueue去管理线程执行请求,当没有任务时等待执行任务,当有任务来的时候就去执行线程,当任务超过时就排队等待。当在列表中去执行任务时,例如在listview中下载图片,为了防止图片错乱,我们用一个ConcurrentHashMap去维护,如果请求时的url和请求后的url是同一个url那么就是对的。并用LruCache去缓存图片。
以下是代码部分。
先看下downLoadImages这个方法,如果缓存中有对应得图片就直接设置,否则去下载。
为什么下载图片时要先 imageView.setImageBitmap(null);执行这句代码呢,由于listview的缓存机制,它会重用缓存的view导致view的图片还是之前缓存的,滑动时图片会覆盖产生一闪一闪的效果,体验很不好。
public class ImageLoderUtils implements HandlerCallbackImp{
private static final ImageLoderUtils mImageLoderUtils = new ImageLoderUtils();
private LruCache<String,Bitmap> mImageCache;
private ThreadManager mThreadManager;
private static final String TAG = "tag";
privateImageLoderUtils(){
mThreadManager = new ThreadManager(this);
mImageCache = mThreadManager.getImageCache();
}
public static ImageLoderUtils getInstance(){
return mImageLoderUtils;
}
public Bitmap getBitmapFromCache(String url){
if(url.isEmpty()){
return null;
}
Bitmap bitmap = mImageCache.get(url);
return bitmap;
}
public void downLoadImages(String imageUrl,final ImageView imageView){
imageView.setImageBitmap(null);
Bitmap cacheBitmap = getBitmapFromCache(imageUrl);
if(cacheBitmap != null){
imageView.setImageBitmap(cacheBitmap);
}else{
BlockQueueParams blockQueueParams = new BlockQueueParams(imageView,imageUrl);
mThreadManager.putRequestQueue(blockQueueParams);
}
}
@Override
public void handlerMsg(Bitmap bitmap,BlockQueueParams params) {
params.imageView.setImageBitmap(bitmap);
}
ImageLoderUtils 是个单例的,创建时会创建一个ThreadManager,ThreadManager管理着线程池。
ThreadManager在创建时就开启了5个线程等待执行任务,当mBlockingQueue.take()取不到数据时线程是阻塞的。当有任务时下载图片,通过Handler去处理UI线程,注意在子线程里Handler要开启一个Looper消息循环啊,isHandlerMsg方法是一个图片错乱校验。
public class ThreadManager {
private BlockingQueue<BlockQueueParams> mBlockingQueue = new LinkedBlockingQueue();
private int mThreadNum = 5;
private LruCache<String, Bitmap> mImageCache;
private HandlerCallbackImp mHandlerCallbackImp;
private ConcurrentHashMap<ImageView,String> map = new ConcurrentHashMap<>();
public ThreadManager(HandlerCallbackImp handlerCallbackImp){
this.mHandlerCallbackImp = handlerCallbackImp;
createThread();
int maxMemory = (int)Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory/2;
mImageCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
private void createThread(){
for (int i = 0 ; i < mThreadNum ; i++){
Thread thread = new Thread(new ThreadRunnable());
thread.start();
}
}
public LruCache<String, Bitmap> getImageCache(){
return mImageCache;
}
public void putRequestQueue(BlockQueueParams blockQueueParams){
try {
mBlockingQueue.put(blockQueueParams);
map.put(blockQueueParams.imageView,blockQueueParams.imageUrl);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class ThreadRunnable implements Runnable{
public String mImageUrl;
public ImageView mImageView;
@Override
public void run() {
while (true)
try {
final BlockQueueParams blockQueueParams = mBlockingQueue.take();
Log.e("thread","thread name = " + Thread.currentThread().getName());
final Bitmap bitmap = DownLoadImagesUtils.downloadBitmap(blockQueueParams.imageUrl);
mImageCache.put(blockQueueParams.imageUrl,bitmap);
this.mImageUrl = blockQueueParams.imageUrl;
this.mImageView = blockQueueParams.imageView;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if(isHandlerMsg()){
mHandlerCallbackImp.handlerMsg(bitmap,blockQueueParams);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean isHandlerMsg(){
if(map.get(mImageView) == mImageUrl){
return true;
}
return false;
}
}
}
下载图片
public class DownLoadImagesUtils {
public static Bitmap downloadBitmap(String imageUrl) {
Bitmap bitmap = null;
HttpURLConnection con = null;
try {
URL url = new URL(imageUrl);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000);
con.setReadTimeout(10 * 1000);
bitmap = BitmapFactory.decodeStream(con.getInputStream());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
}
return bitmap;
}
}
处理handler回调的接口
public interface HandlerCallbackImp {
public void handlerMsg(Bitmap bitmap,BlockQueueParams params);
}
队列里放的任务参数
public class BlockQueueParams{
public ImageView imageView;
public String imageUrl;
public BlockQueueParams(ImageView imageView, String imageUrl) {
this.imageView = imageView;
this.imageUrl = imageUrl;
}
}
最后使用
ImageLoderUtils imageLoderUtils = ImageLoderUtils.getInstance();
imageLoderUtils.downLoadImages(imageUrl,imageView);
打印了一下线程使用情况发现只是这5个线程在循环工作。