Android ListView异步加载图片优化

废话不多说,异步请求多线程。

优化:

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地址来判断图片的唯一性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值