【OSC手机App技术解析】- 列表异步线程加载图片

迷途d书童 发布于 2012/06/29 13:38
阅读 1K+
收藏 12
手机客户端以列表形式展示数据是非常常见的一种方式。然而列表中要显示图片(比如:头像)就要采用异步线程加载的方式,这样做是为了防止加载图片数据的时候,花费时间过长,阻塞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);

如果大家有什么疑问的话,欢迎在下面回帖一起探讨。

PS:

OSC Android客户端下载地址: http://www.oschina.net/uploads/osc.apk
OSC iPhone客户端下载地址:
http://www.oschina.net/uploads/osc.ipa
OSC Windows Phone客户端下载地址: http://www.oschina.net/uploads/osc.xap

加载中
0
FoxHu
FoxHu
不错,谢谢分享!
0
shikeaiDev
shikeaiDev
非常不错的思想,貌似哪里见到过的。。。。?
0
x
xusong

ImageUtils

FileUtils

ApiClient

还有这三个类的源码可以看一下不

返回顶部
顶部