标签:多线程 线程池 threadpoolexcutor 图片异步加载
在上一篇专题Android开发之图片处理专题(二):利用AsyncTask和回调接口实现图片的异步加载和压缩中我们实现了listView的图片的大量加载。今天,我们换一种方式,采用线程池的方式来实现。
我们需要准备两个东西:
1、图片下载任务类
2、线程池。
/**
* @ClassName: AsyncImageTask
* @author victor_freedom (x_freedom_reddevil@126.com)
* @createddate 2015-2-1 下午10:58:52
* @Description: 图片异步加载任务
*/
public class AsyncImageTask implements Runnable {
private final String TAG = "AsyncImageTask";
// 任务标识ID,其实就是图片缓存路径
private String taskId;
// 传入需要设定的ImageView
private ImageView imageView;
// 图片下载路径
private String destUrl;
// 下载失败显示图片的ID
private int failImage = -1;
// 默认图片ID
private int defaultImage = -1;
// 压缩比例
private int sampleSize = 1;
// 是否设置为background
private boolean forBackground = false;
private boolean canceled = false;
// 消息管理器
private AsyncHandler handler = null;
// 动画
private Animation animation = null;
public final static int OK = 1;
public final static int FAIL = 2;
public final static int EXSIT = 3;
private boolean forHome = false;
/**
* 构造函数
*
* @param imageView
* ImageView
* @param url
* 图片地址
*/
public AsyncImageTask(ImageView imageView, String url) {
this(null, imageView, url);
}
/**
* 构造函数
*
* @param taskId
* 任务id
* @param imageView
* ImageView
* @param url
* 图片地址
*/
public AsyncImageTask(String taskId, ImageView imageView, String url) {
if (null == taskId || taskId.trim().length() == 0) {
// 任务标识用图片缓存地址来标识唯一
String tid = CacheName.getCachePath(imageView.getContext(), url);
this.taskId = tid;
} else {
this.taskId = taskId;
}
if (null != imageView) {
this.imageView = imageView;
this.imageView.setTag(this.taskId);
}
this.destUrl = url;
this.handler = new AsyncHandler();
}
/**
* taskId
*
* @return the taskId
*/
public String getTaskId() {
return taskId;
}
/**
* @param taskId
* the taskId to set
*/
public void setTaskId(String taskId) {
this.taskId = taskId;
}
/**
* forHome
*
* @return the forHome
*/
public boolean isForHome() {
return forHome;
}
/**
* @param forHome
* the forHome to set
*/
public void setForHome(boolean forHome) {
this.forHome = forHome;
}
/**
* defaultImage
*
* @return the defaultImage
*/
public int getDefaultImage() {
return defaultImage;
}
/**
* @param defaultImage
* the defaultImage to set
*/
public void setDefaultImage(int defaultImage) {
this.defaultImage = defaultImage;
}
/**
* 获取下载失败显示的图片资源id
*
* @return int
*/
public int getFailImage() {
return failImage;
}
/**
* 设置下载失败后要显示图片资源id
*
* @param failImage
* R.drawable.xxx
*/
public void setFailImage(int failImage) {
this.failImage = failImage;
}
/**
* 获取图片的压缩比例
*
* @return int
*/
public int getSampleSize() {
return sampleSize;
}
/**
* 设置图片的采样率
*
* @param sampleSize
* 最好为2的指数倍
*/
public void setSampleSize(int sampleSize) {
if (sampleSize > 0) {
this.sampleSize = sampleSize;
}
}
/**
* 下载图片后是否设置为背景图
*
* @return 是背景图返回true,否则返回false
*/
public boolean isForBackground() {
return forBackground;
}
/**
* 设置是否显示为背景图
*
* @param forBackground
* 默认为false
*/
public void setForBackground(boolean forBackground) {
this.forBackground = forBackground;
}
/**
* canceled
*
* @return the canceled
*/
public boolean isCanceled() {
return canceled;
}
/**
* 取消当前任务
*
* @param canceled
* the canceled to set
*/
public void setCanceled(boolean canceled) {
this.canceled = canceled;
}
/**
* imageView
*
* @return the imageView
*/
public ImageView getImageView() {
return imageView;
}
/**
* @param imageView
* the imageView to set
*/
public void setImageView(ImageView imageView) {
this.imageView = imageView;
}
/**
* destUrl
*
* @return the destUrl
*/
public String getDestUrl() {
return destUrl;
}
/**
* @param destUrl
* the destUrl to set
*/
public void setDestUrl(String destUrl) {
this.destUrl = destUrl;
}
/**
* handler
*
* @return the handler
*/
public AsyncHandler getHandler() {
return handler;
}
/**
* @param handler
* the handler to set
*/
public void setHandler(AsyncHandler handler) {
this.handler = handler;
}
/**
* animation
*
* @return the animation
*/
public Animation getAnimation() {
return animation;
}
/**
* 设置图片下载成功后的动画效果
*
* @param animation
* the animation to set
*/
public void setAnimation(Animation animation) {
this.animation = animation;
}
@Override
public void run() {
// 拿到缓存地址
String destPath = CacheName.getCachePath(imageView.getContext(),
destUrl);
// 创建文件
File file = new File(destPath);
Message msg = handler.obtainMessage();
// 判断该文件是否已经下载过
if (!file.exists()) {
// 如果没有,则下载
if (download(destUrl, destPath + ".tmp")) {
// 创建临时缓存文件
File temp = new File(destPath + ".tmp");
// 按照下载文件命名
temp.renameTo(file);
msg.what = OK;
msg.obj = destPath;
} else {
// 如果下载失败,删除临时文件
msg.what = FAIL;
deleteTemp(destPath + ".tmp");
}
} else {
msg.what = EXSIT;
msg.obj = destPath;
}
if (!canceled) {
handler.sendMessage(msg);
} else {
// 如果中断了下载,则清除临时文件
deleteTemp(destPath + ".tmp");
}
}
private void deleteTemp(String path) {
File file = new File(path);
if (file.exists()) {
file.delete();
}
}
private boolean download(String imageUrl, String destPath) {
// 删除同名临时文件
deleteTemp(destPath);
boolean success = false;
URL url = null;
InputStream is = null;
OutputStream out = null;
HttpURLConnection conn = null;
// 下载 图片
try {
url = new URL(imageUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(20000);
conn.setReadTimeout(5 * 60 * 1000);
conn.setDoInput(true);
conn.setRequestProperty("Accept-Language", "zh-cn");
conn.setRequestProperty(
"User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.connect();
if (conn.getResponseCode() == 200) {
is = conn.getInputStream();
int read = 0;
byte[] buffer = new byte[1024];
// 拿到缓存路径的输入流
out = new FileOutputStream(destPath);
while ((read = is.read(buffer)) != -1) {
// 写入数据
out.write(buffer, 0, read);
}
// 返回成功标识位
success = true;
} else {
Log.d(TAG, "the respond code is ---> " + conn.getResponseCode());
Log.d(TAG, "the url is:" + imageUrl);
}
} catch (MalformedURLException e) {
Log.d(TAG, "MalformedURLException ---> " + e.toString());
} catch (IOException e) {
Log.d(TAG, "IOException ---> " + e.toString());
} finally {
try {
if (out != null) {
out.flush();
out.close();
}
if (conn != null) {
conn.disconnect();
}
} catch (IOException e) {
Log.d(TAG, e.toString());
}
}
return success;
}// end download
/**
* @ClassName: AsyncHandler
* @author victor_freedom (x_freedom_reddevil@126.com)
* @createddate 2015-2-1 下午11:16:28
* @Description: 消息首发器
*/
final class AsyncHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case FAIL:
doFail(msg);
break;
default:
if (forHome) {
imageView.setScaleType(ScaleType.FIT_XY);
}
doSuccess(msg);
break;
}
}
/**
* @Title: doFail
* @Description: 下载失败后,设定指定的失败图片
* @param msg
* @throws
*/
private void doFail(Message msg) {
if (forBackground && failImage != -1) {
imageView.setBackgroundResource(failImage);
} else if (!forBackground && failImage != -1) {
imageView.setImageResource(failImage);
}
}
/**
* @Title: doSuccess
* @Description: 下载成功后,显示
* @param msg
* @throws
*/
private void doSuccess(Message msg) {
//拿到路径
String path = (String) msg.obj;
//压缩存放图片
BitmapManager.getInstance().putBitmap(path, sampleSize);
//拿到图片
Bitmap bitmap = BitmapManager.getInstance().getBitmap(path);
String tag = (String) imageView.getTag();
//图片设定
if ((null != bitmap) && tag == null || tag.equals(taskId)) {
if (forBackground) {
imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));
} else if (!forBackground) {
imageView.setImageBitmap(bitmap);
}
//是否执行动画
if (msg.what == OK && null != animation) {
imageView.setAnimation(animation);
animation.start();
}
}
}
}// end AsyncHandler
}/**
* @ClassName: TaskQueue
* @author victor_freedom (x_freedom_reddevil@126.com)
* @createddate 2015-2-1 下午11:31:19
* @Description: 图片异步任务管理器
*/
public final class TaskQueue {
private final static String TAG = "TaskQueue";
private static TaskQueue taskQueue = null;
private static ThreadPoolExecutor threadPool = null;// 线程池
private final static int CORE_SIZE = 2; // 最小工作线程数量
private final static int MAX_SIZE = 3; // 最大共作线程数量
private final static int QUEUE_SIZE = 20; // 线程缓冲队列容量
private final static long ALIVE_TIME = 10; // 某线程允许的最大空闲时长
private final static TimeUnit T_Unit = TimeUnit.SECONDS; // 空闲时长单位:秒
private static BlockingQueue<Runnable> queue = null; // 线程缓冲队列
private static RejectedExecutionHandler rejectedHandler = new DiscardOldestPolicy(); // 线程池拒绝策略,当缓冲队列满时,工作队列头部的任务将被删除
//新建任务队列及线程池
private TaskQueue() {
queue = new LinkedBlockingQueue<Runnable>(QUEUE_SIZE);
threadPool = new ThreadPoolExecutor(CORE_SIZE, MAX_SIZE, ALIVE_TIME,
T_Unit, queue, rejectedHandler);
}
/**
* 采用单例设计
*
* @return TaskQueue
*/
public static TaskQueue getInstance() {
if (null == taskQueue) {
taskQueue = new TaskQueue();
}
return taskQueue;
}
/**
* 添加一个任务
*
* @param task
* AsyncImageTask
*/
public void addTask(AsyncImageTask task) {
//检查本地是否已经存在缓存文件
if (!hadLocal(task)) {
boolean had = false;
//判断任务队列里面是否已经存在该任务
for (int i = 0; i < queue.size(); i++) {
AsyncImageTask t = (AsyncImageTask) queue.element();
if (task.getTaskId().equals(t.getTaskId())) {
had = true;
Log.d(TAG, "the task id is:" + t.getTaskId());
break;
}
}
if (!had) {
//如果不存在,加入执行队列
if (task.getDefaultImage() != -1) {
if (!task.isForBackground()) {
task.getImageView().setImageResource(
task.getDefaultImage());
} else {
task.getImageView().setBackgroundResource(
task.getDefaultImage());
}
}
threadPool.execute(task);
} else {
//如果已经存在,则不做修改。等待执行
if (task.getDefaultImage() != -1) {
if (!task.isForBackground()) {
task.getImageView().setImageResource(
task.getDefaultImage());
} else {
task.getImageView().setBackgroundResource(
task.getDefaultImage());
}
}
}
} else {
//如果有缓存,则直接拿本地的图片来用
String destPath = CacheName.getCachePath(task.getImageView()
.getContext(), task.getDestUrl());
Message msg = new Message();
msg.what = AsyncImageTask.EXSIT;
msg.obj = destPath;
task.getHandler().sendMessage(msg);
}
}
/**
* 检查本地是否已经存在缓存文件
*
* @param task
* @return 存在返回true,否则返回false
*/
private boolean hadLocal(AsyncImageTask task) {
String destPath = CacheName.getCachePath(task.getImageView()
.getContext(), task.getDestUrl());
File file = new File(destPath);
if (file.exists()) {
if (ImageCheck.isAvailable(destPath)) {
file.setLastModified(System.currentTimeMillis());
return true;
} else {
file.delete();
}
}
return false;
}
/**
* 关闭所有工作线程
*/
public void shutDown() {
threadPool.shutdown();
taskQueue = null;
queue = null;
}
}
//拿到路径 String url = Constant.PIC_BASE_URL + news.PicUrl; //创建任务 AsyncImageTask task = new AsyncImageTask(holder.iv, url); //设定默认图片和下载失败图片 task.setDefaultImage(R.drawable.news_image_default); task.setFailImage(R.drawable.news_image_default); //加入到任务队列中执行 TaskQueue.getInstance().addTask(task);
Android开发之图片处理专题(三):利用ThreadPoolExcutor线程池实现多图片的异步加载
标签:多线程 线程池 threadpoolexcutor 图片异步加载
原文地址:http://blog.csdn.net/victorfreedom/article/details/43378271