本文是实现一个图片异步加载器。只需直接调用以下代码网络异步加载图片。
ImageLoader imageLoader = new ImageLoader(context); //生成图片加载器
imageLoader.DisplayImage(imgUrl,imageView); //异步加载图片,imgUrl为图片的URL,imageView为图片控件
具体实现:
1)文件缓存类:主要配置缓存文件存放地址。
/**
* 文件缓存类
*
* @version 1.0
* @created 2013-5-8
*/
public class FileCache {
private File cacheDir;//文件缓存目录
public FileCache(Context context){
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
//如果有SD卡则设定 卡内“/cache/”为缓存目录
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"/cache/");
else{
//如果无SD卡则设定手机内为缓存目录
cacheDir=context.getCacheDir();
String path=context.getCacheDir().getAbsolutePath();
}
if(!cacheDir.exists())
cacheDir.mkdirs();
}
//通过网络图片的URL,映射相应的文件。
public File getFile(String url){
String filename=String.valueOf(url.hashCode()); //以url的哈希码为文件名
//String filename = URLEncoder.encode(url); //另一种方法可以选择
File f = new File(cacheDir, filename);
return f;
}
//清除文件缓存
public void clear(){
File[] files=cacheDir.listFiles();
if(files==null)
return;
for(File f:files)
f.delete();
}
}
2)内存缓存类:用哈希表存储图片,采用LRU方法管理缓存,限定为系统空闲内存的1/4
/**
* 内存缓存类
*
* @version 1.0
* @created 2013-5-8
*/
public class MemoryCache {
private static final String TAG = "MemoryCache"; //log标志
private Map<String, Bitmap> cache=Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));
//内存缓存
//LinkedHashMap为生成遍历时有序的哈希表,便于实现LRU,既最近最少使用算法
//Collections.synchronizedMap用于此哈希表对多线程调用,既让此哈希表多线程操作是安全的
private long size=0; //分配的内存大小
private long limit=1000000;//最大分配内存限定,单位为字节bytes
public MemoryCache(){
//设定当前系统的1/4的内存为最大分配内存
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
//查找图片
public Bitmap get(String url){
try{
if(!cache.containsKey(url))
return null;
return cache.get(url);
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
//删除图片
public void reMove(String url){
try{
if(!cache.containsKey(url))
return;
cache.remove(url);
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
//增加图片到内存缓存
public void put(String url, Bitmap bitmap){
try{
if(cache.containsKey(url))
size-=getSizeInBytes(cache.get(url));
cache.put(url, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();//检查内存是否到限定的最大值
}catch(Throwable th){
th.printStackTrace();
}
}
//检查内存是否到限定的最大值,如果到了,删除最早的图片
private void checkSize() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
if(size>limit){
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();
//遍历缓存,删除最早的图片,直到大小小于最大值
while(iter.hasNext()){
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
//清除缓存
public void clear() {
try{
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
//计算图片的大小
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
3)图片加载器:加载图片,预先显示的默认的图片,网络下载完成将自动刷新图片显示,加载图片将限定大小(节省空间,防止OOM)。默认同时加载5张图片。
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache(); //内存缓存
FileCache fileCache; //文件缓存
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
//管理需加载的ImageView控件
//WeakHashMap以弱键实现的基于哈希表的 Map,当ImageView控件不正常使用时,其键值对将丢弃
//Collections.synchronizedMap方法进行线程级安全控制
ExecutorService executorService;//线程池控制网络下载图片
Handler handler=new Handler();//handler用于显示图片(子线程通过Handler才能控制UI操作)
Context mContext;
final int stub_id = R.drawable.network_error; //ImageView控件默认显示的图片
final int REQUIRED_SIZE = 145; //设定图片长或宽的最大值,其单位为像素,
//其为图片下载后显示的长,宽都要小于此值
public ImageLoader(Context context){
mContext = context;
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5); //最多5个线程的线程池
}
//异步加载图片的调用
public void DisplayImage(String url, ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);//查找内存缓存
if(bitmap!=null)
imageView.setImageBitmap(bitmap);//如果内存缓存中有此图片,直接显示图
else
{
queuePhoto(url, imageView); //内存缓存中无此图片,网络加载图片
imageView.setImageResource(stub_id); //先显示默认图片
}
}
//加载图片
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView); //生成图片加载信息类
executorService.submit(new PhotosLoader(p));//生成子线程图片加载任务类,并调用子线程
}
//图片加载信息类
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public boolean isDown; //是否下载
public PhotoToLoad(String u, ImageView i){
url=u;
imageView=i;
isDown = false;
}
public PhotoToLoad(String u, ImageView i,boolean down){
url=u;
imageView=i;
isDown = down;
}
}
//子线程图片加载任务类
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad; //
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
@Override
public void run() {
try{
if(imageViewReused(photoToLoad))
return; //如果imageViewsm无此条目,无需加载(当ImageView控件不正常使用时,其键值对将丢弃)
Bitmap bmp=getBitmap(photoToLoad.url); //网络下载图片
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;//如果imageViewsm无此条目,无需加载(当ImageView控件不正常使用时,其键值对将丢弃)
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
handler.post(bd); //控制UI加载图片
}catch(Throwable th){
th.printStackTrace();
}
}
}
private Bitmap getBitmap(String url)
{
File f=fileCache.getFile(url); //文件缓存图片
Bitmap b = decodeFile(f); //从文件中加载图片
if(b!=null){
return b; //如果文件缓存中有此图片,直接用此图
}
//网络下载图片
try {
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000); //连接超时30秒
conn.setReadTimeout(30000); //读取超时30秒
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream(); //网络流输入
OutputStream os = new FileOutputStream(f); //文件流输出
CopyStream(is, os); //拷贝图片的网络数据到文件
os.close();
conn.disconnect();
bitmap = decodeFile(f); //读取已下载的图片
return bitmap;
} catch (Throwable ex){
ex.printStackTrace();
if(ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
//判断imageViews是否有此条目
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//控制图片控件显示图片
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
//清除缓存
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
//文件读取图片,读取图片将按比例缩小,来达到REQUIRED_SIZE
public Bitmap decodeFile(File f){
try {
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true; //设定此值将不编码图片,而获得宽,高
FileInputStream stream1=new FileInputStream(f);
BitmapFactory.decodeStream(stream1,null,o);
stream1.close();
//Find the correct scale value. It should be the power of 2.
int width_tmp=o.outWidth, height_tmp=o.outHeight; //获得宽,高
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2; //计算缩放值,使宽,高小于REQUIRED_SIZE
}
//根据缩放值编码图片
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
FileInputStream stream2=new FileInputStream(f);
Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void CopyStream(InputStream is, OutputStream os){
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}
}