【OSC手機App技術解析】- 列表異步線程加載圖片

手機客戶端以列表形式展示數據是非常常見的一種方式。然而列表中要顯示圖片(比如:頭像)就要採用異步線程加載的方式,這樣做是爲了防止加載圖片數據的時候,花費時間過長,阻塞UI線程,從而達到保持App的流暢性的目的。

下面我將分享 OSChina.NET Android版客戶端的列表異步線程加載圖片的方法:
 

 


 

圖片緩存

private static HashMap<String, SoftReference<Bitmap>> cache;  

圖片緩存是當有加載過相同的圖片的時候,可以快速重複使用,比如同一個人的頭像。

圖片控件集合

private static Map<ImageView, String> p_w_picpathViews;  

圖片控件集合是一個Map,記錄當前ImageView控件對應的圖片地址,用來防止異步線程加載圖片時候ImageView控件顯示的圖片與實際圖片地址對應的圖片不符,出現錯亂。

線程池

private static ExecutorService pool;  

固定線程池裏的併發線程數,可以防止用戶在快速滑動列表的時候,不執行已經滑過去的加載線程。

具體的初始化代碼:

 

static {  
        cache = new HashMap<String, SoftReference<Bitmap>>();  
        pool = Executors.newFixedThreadPool(5);  //固定線程池
        p_w_picpathViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    }

接下來,我們來看看具體是如何加載圖片的:

 

public void loadBitmap(String url, ImageView p_w_picpathView, Bitmap defaultBmp, int width, int height) {  
        p_w_picpathViews.put(p_w_picpathView, url);  
        Bitmap bitmap = getBitmapFromCache(url);  
   
        if (bitmap != null) {  
	    //顯示緩存圖片
            p_w_picpathView.setImageBitmap(bitmap);  
        } else {  
        	//加載SD卡中的圖片緩存
        	String filename = FileUtils.getFileName(url);
        	String filepath = p_w_picpathView.getContext().getFilesDir() + File.separator + filename;
    		File file = new File(filepath);
    		if(file.exists()){
			//顯示SD卡中的圖片緩存
    			Bitmap bmp = ImageUtils.getBitmap(p_w_picpathView.getContext(), filename);
        		p_w_picpathView.setImageBitmap(bmp);
        	}else{
			//線程加載網絡圖片
        		p_w_picpathView.setImageBitmap(defaultBmp);
        		queueJob(url, p_w_picpathView, 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> p_w_picpathViews;  
    private Bitmap defaultBmp;  
    
    static {  
        cache = new HashMap<String, SoftReference<Bitmap>>();  
        pool = Executors.newFixedThreadPool(5);  //固定線程池
        p_w_picpathViews = 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 p_w_picpathView
     */
    public void loadBitmap(String url, ImageView p_w_picpathView) {  
    	loadBitmap(url, p_w_picpathView, this.defaultBmp, 0, 0);
    }
	
    /**
     * 加載圖片-可設置加載失敗後顯示的默認圖片
     * @param url
     * @param p_w_picpathView
     * @param defaultBmp
     */
    public void loadBitmap(String url, ImageView p_w_picpathView, Bitmap defaultBmp) {  
    	loadBitmap(url, p_w_picpathView, defaultBmp, 0, 0);
    }
    
    /**
     * 加載圖片-可指定顯示圖片的高寬
     * @param url
     * @param p_w_picpathView
     * @param width
     * @param height
     */
    public void loadBitmap(String url, ImageView p_w_picpathView, Bitmap defaultBmp, int width, int height) {  
        p_w_picpathViews.put(p_w_picpathView, url);  
        Bitmap bitmap = getBitmapFromCache(url);  
   
        if (bitmap != null) {  
	    //顯示緩存圖片
            p_w_picpathView.setImageBitmap(bitmap);  
        } else {  
            //加載SD卡中的圖片緩存
            String filename = FileUtils.getFileName(url);
            String filepath = p_w_picpathView.getContext().getFilesDir() + File.separator + filename;
    	    File file = new File(filepath);
    	    if(file.exists()){
		//顯示SD卡中的圖片緩存
    		Bitmap bmp = ImageUtils.getBitmap(p_w_picpathView.getContext(), filename);
        	p_w_picpathView.setImageBitmap(bmp);
            }else{
		//線程加載網絡圖片
        	p_w_picpathView.setImageBitmap(defaultBmp);
        	queueJob(url, p_w_picpathView, 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 p_w_picpathView
     * @param width
     * @param height
     */
    public void queueJob(final String url, final ImageView p_w_picpathView, final int width, final int height) {   
        final Handler handler = new Handler() {  
            public void handleMessage(Message msg) {  
                String tag = p_w_picpathViews.get(p_w_picpathView);  
                if (tag != null && tag.equals(url)) {  
                    if (msg.obj != null) {  
                        p_w_picpathView.setImageBitmap((Bitmap) msg.obj);  
                        try {
                            //向SD卡中寫入圖片緩存
			    ImageUtils.saveImage(p_w_picpathView.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(p_w_picpathURL, p_w_picpathView);

如果大家有什麼疑問的話,歡迎在下面回帖一起探討。

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

 

轉載:http://www.oschina.net/question/157182_59454

 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章