废话不多说,异步请求多线程。
优化:
1.线程池 2.缓存到内存 3.缓存到SD卡 4.并发下防止重复请求(少数情况下)
主要用到 ExecutorService、SoftReference<Bitmap>、Future<SoftReference<Bitmap>>(防止重复线程请求)、ConcurrentHashMap<String, Future<SoftReference<Bitmap>>>()
package com.cntomorrow.tboing.imageloader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ImageView;
import com.tby.tboing.http.HttpRequestService;
public class AsyncBufferImageLoader {
private final static String LOG_TAG= "ImageLoader";
private final ConcurrentHashMap<String, Future<SoftReference<Bitmap>>> imageCache = new ConcurrentHashMap<String, Future<SoftReference<Bitmap>>>();
//还是根据手机配置来吧
private ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 3);
@SuppressLint("SdCardPath")
static final private String DIR = "/mnt/sdcard/Demo01/";
public AsyncBufferImageLoader() {
}
/**
* <p> 加载图片</p>
* @param imageView
* @param imageURL
* @param imageCallBack
* @param externalLoad 是否允许从SD卡或服务器获取图片
* @return <br><br>
* author tboing<BR>
* date 2013-4-1<br>
* remark <br>
*/
public Bitmap loadBitmap(final ImageView imageView, final String imageURL,
final ImageCallBack imageCallBack, boolean externalLoad) {
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.v(LOG_TAG, "从外部拿到了数据!");
imageCallBack.imageLoad(imageView, (Bitmap)msg.obj, imageURL);
super.handleMessage(msg);
}
};
Future<SoftReference<Bitmap>> f = imageCache.get(imageURL);
Bitmap bitmap = null;
if (f != null) {
Log.v(LOG_TAG, "缓存中有值,从缓存中取!");
SoftReference<Bitmap> reference;
try {
if (f.isDone()) { // 如果结果已经返回则直接取,不会阻塞
reference = f.get();
bitmap = reference.get();
if (bitmap != null) {
Log.v(LOG_TAG, "缓存中取成功!");
return bitmap;
}
Log.v(LOG_TAG, "缓存中取失败,将从SD卡或者服务器获取!");
imageCache.remove(imageURL, f);
// 如果弱引用失效则需要重新去请求
} else {
Log.v(LOG_TAG, "已经有线程请求图片,但未完成,请稍后!----------------------");
}
} catch (InterruptedException e) {
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(!externalLoad) return null;
executors.execute(new RequestImage(imageURL, handler));
return null;
}
private class RequestImage implements Runnable {
private String imageURL;
private Handler handler;
public RequestImage(String imageURL, Handler handler) {
this.imageURL = imageURL;
this.handler = handler;
}
@Override
public void run() {
Future<SoftReference<Bitmap>> f = null;
Bitmap bitmap = null;
//如果不需要从SD卡或者NET请求
FutureTask<SoftReference<Bitmap>> ft = new FutureTask<SoftReference<Bitmap>>(
new Callable<SoftReference<Bitmap>>() {
@Override
public SoftReference<Bitmap> call() throws Exception {
Bitmap bitmap = readFromSDCard(imageURL); //从SD卡中获取
if (bitmap == null) {
Log.v(LOG_TAG, "SD卡中没取着!");
// 如果不在内存缓存中,也不在本地(被jvm回收掉),则开启线程下载图片
HttpRequestService httpRequest = new HttpRequestService();
try {
bitmap = httpRequest.requestBitmap(imageURL);
Thread.sleep(1000);
if (bitmap != null) {
writeToSDCard(imageURL, bitmap); //写入SD卡
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return new SoftReference<Bitmap>(bitmap);
}
});
//防止并发覆盖
f = imageCache.putIfAbsent(imageURL, ft);
if (f == null) {
f = ft;
ft.run();
} else {
Log.v(LOG_TAG, "其他线程正在请求!--------------------");
}
try {
bitmap = f.get().get();
} catch (Exception e) {
imageCache.remove(imageURL, ft);
e.printStackTrace();
}
sendMessage(bitmap);
}
private void sendMessage(Bitmap bitmap) {
Message msg = handler.obtainMessage(0, bitmap);
handler.sendMessage(msg);
}
}
private Bitmap readFromSDCard(String imageURL) {
String bitmapName = imageURL
.substring(imageURL.lastIndexOf("/") + 1);
File cacheDir = new File(DIR);
File[] cacheFiles = cacheDir.listFiles();
if(cacheFiles != null) {
int i = 0;
for (; i < cacheFiles.length; i++) {
if (bitmapName.equals(cacheFiles[i].getName())) {
break;
}
}
if (i < cacheFiles.length) {
return BitmapFactory.decodeFile(DIR + bitmapName);
}
}
return null;
}
private void writeToSDCard(String imageURL, Bitmap bitmap) {
File dir = new File(DIR);
if (!dir.exists()) {
dir.mkdirs();
}
File bitmapFile = new File(DIR
+ imageURL.substring(imageURL.lastIndexOf("/") + 1));
if (!bitmapFile.exists()) {
try {
bitmapFile.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileOutputStream fos;
try {
fos = new FileOutputStream(bitmapFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 回调接口
*
* @author onerain
*
*/
public interface ImageCallBack {
public void imageLoad(ImageView imageView, Bitmap bitmap, String url);
}
public void shutdown() {
if(executors != null) {
executors.shutdown();
executors = null;
}
imageCache.clear();
}
}
其实如果说不存在重复图片也就不存在重复请求问题,这里是根据URL地址来判断图片的唯一性