手机客户端以列表形式展示数据是非常常见的一种方式。然而列表中要显示图片(比如:头像)就要采用异步线程加载的方式,这样做是为了防止加载图片数据的时候,花费时间过长,阻塞UI线程,从而达到保持App的流畅性的目的。
下面我将分享 OSChina.NET Android版客户端的列表异步线程加载图片的方法:
图片缓存
private static HashMap<String, SoftReference<Bitmap>> cache;
图片缓存是当有加载过相同的图片的时候,可以快速重复使用,比如同一个人的头像。
图片控件集合
private static Map<ImageView, String> imageViews;
图片控件集合是一个Map,记录当前ImageView控件对应的图片地址,用来防止异步线程加载图片时候ImageView控件显示的图片与实际图片地址对应的图片不符,出现错乱。
线程池
private static ExecutorService pool;
固定线程池里的并发线程数,可以防止用户在快速滑动列表的时候,不执行已经滑过去的加载线程。
具体的初始化代码:
static { cache = new HashMap<String, SoftReference<Bitmap>>(); pool = Executors.newFixedThreadPool(5); //固定线程池imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());}
接下来,我们来看看具体是如何加载图片的:
public void loadBitmap(String url, ImageView imageView, Bitmap defaultBmp, int width, int height) { imageViews.put(imageView, url); Bitmap bitmap = getBitmapFromCache(url); if (bitmap != null) { //显示缓存图片imageView.setImageBitmap(bitmap); } else { //加载SD卡中的图片缓存String filename = FileUtils.getFileName(url);String filepath = imageView.getContext().getFilesDir() + File.separator + filename;File file = new File(filepath);if(file.exists()){//显示SD卡中的图片缓存Bitmap bmp = ImageUtils.getBitmap(imageView.getContext(), filename);imageView.setImageBitmap(bmp);}else{//线程加载网络图片imageView.setImageBitmap(defaultBmp);queueJob(url, imageView, width, height);}} }
上面的代码中,我们根据图片的url地址,先从图片缓存里面查找是否已缓存过,如果没有,再从SD卡的图片缓存文件中查找,如果再没有,最后才是加载网络图片。这才是以最快速的方式显示图片。
下面,我将贴出完整的代码:
/*** 异步线程加载图片工具类* @author liux*/
public class BitmapManager { private static HashMap<String, SoftReference<Bitmap>> cache; private static ExecutorService pool; private static Map<ImageView, String> imageViews; private Bitmap defaultBmp; static { cache = new HashMap<String, SoftReference<Bitmap>>(); pool = Executors.newFixedThreadPool(5); //固定线程池imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());} public BitmapManager(){}public BitmapManager(Bitmap def) {this.defaultBmp = def;}/*** 设置默认图片* @param bmp*/public void setDefaultBmp(Bitmap bmp) { defaultBmp = bmp; } /*** 加载图片* @param url* @param imageView*/public void loadBitmap(String url, ImageView imageView) { loadBitmap(url, imageView, this.defaultBmp, 0, 0);}/*** 加载图片-可设置加载失败后显示的默认图片* @param url* @param imageView* @param defaultBmp*/public void loadBitmap(String url, ImageView imageView, Bitmap defaultBmp) { loadBitmap(url, imageView, defaultBmp, 0, 0);}/*** 加载图片-可指定显示图片的高宽* @param url* @param imageView* @param width* @param height*/public void loadBitmap(String url, ImageView imageView, Bitmap defaultBmp, int width, int height) { imageViews.put(imageView, url); Bitmap bitmap = getBitmapFromCache(url); if (bitmap != null) { //显示缓存图片imageView.setImageBitmap(bitmap); } else { //加载SD卡中的图片缓存String filename = FileUtils.getFileName(url);String filepath = imageView.getContext().getFilesDir() + File.separator + filename;File file = new File(filepath);if(file.exists()){//显示SD卡中的图片缓存Bitmap bmp = ImageUtils.getBitmap(imageView.getContext(), filename);imageView.setImageBitmap(bmp);}else{//线程加载网络图片imageView.setImageBitmap(defaultBmp);queueJob(url, imageView, width, height);}} } /*** 从缓存中获取图片* @param url*/public Bitmap getBitmapFromCache(String url) { Bitmap bitmap = null;if (cache.containsKey(url)) { bitmap = cache.get(url).get(); } return bitmap; } /*** 从网络中加载图片* @param url* @param imageView* @param width* @param height*/public void queueJob(final String url, final ImageView imageView, final int width, final int height) { final Handler handler = new Handler() { public void handleMessage(Message msg) { String tag = imageViews.get(imageView); if (tag != null && tag.equals(url)) { if (msg.obj != null) { imageView.setImageBitmap((Bitmap) msg.obj); try {//向SD卡中写入图片缓存ImageUtils.saveImage(imageView.getContext(), FileUtils.getFileName(url), (Bitmap) msg.obj);} catch (IOException e) {e.printStackTrace();}} } } }; pool.execute(new Runnable() { public void run() { Message message = Message.obtain(); message.obj = downloadBitmap(url, width, height); handler.sendMessage(message); } }); } /*** 下载图片-可指定显示图片的高宽* @param url* @param width* @param height*/private Bitmap downloadBitmap(String url, int width, int height) { Bitmap bitmap = null;try {//http加载图片bitmap = ApiClient.getNetBitmap(url);if(width > 0 && height > 0) {//指定显示图片的高宽bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);} //放入缓存cache.put(url, new SoftReference<Bitmap>(bitmap));} catch (Exception e) {e.printStackTrace();}return bitmap; }
}
工具类使用
实例化时,可以设置默认的显示图片:
BitmapManager bmpManager = new BitmapManager(BitmapFactory.decodeResource(context.getResources(), R.drawable.loading));
调用加载图片的方法:
bmpManager.loadBitmap(imageURL, imageView);