最近項目中碰到了多線程下載圖片的問題,我自己寫了一個工具類 ImageLoader, 該工具類支援圖片緩衝:有SDCARD緩衝,記憶體緩衝等,支援多線程下載,其具體實現思路為:
1、實現一個線程池 ExecutorService,將下載多線程的任務添加到該線程池中,可以定義線程池的大小
2、下載到圖片成功之後,如何通知UI線程去更新UI
3、圖片的緩衝處理等。
其核心代碼如下:
import java.io.File;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;import android.text.TextUtils;import android.widget.ImageView;import com.giant.lib.utils.GiantLog;import giant.activity.R;public class ImageLoader { final int stub_id = R.drawable.default_user_ico; MemoryCache memoryCache = new MemoryCache(); FileCache fileCache; private Map<ImageView, String> imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); ExecutorService executorService; private static final String TAG = "ImageLoader"; public ImageLoader(Context context){ fileCache = new FileCache(context); executorService = Executors.newFixedThreadPool(5); } /** * 顯示圖片需要預設圖片 * @param url * @param imageView */ public void DisplayImage(String url, ImageView imageView) { imageViews.put(imageView, url); Bitmap bitmap = memoryCache.get(url); if(TextUtils.isEmpty(url)){ imageView.setImageResource(R.drawable.default_user_ico); }// Bitmap bitmap = null; if(bitmap!= null) { //先載入記憶體中的緩衝 imageView.setImageBitmap(bitmap); GiantLog.d(TAG, "load image data form memory"); } else { queuePhoto(url,imageView); imageView.setImageResource(stub_id); } } /** * 顯示圖片不需要預設圖片 * @param url * @param imageView */ public void DisplayImageWithNodefault(String url, ImageView imageView) { imageViews.put(imageView, url); Bitmap bitmap = memoryCache.get(url);// Bitmap bitmap = null; if(bitmap!= null) { //先載入記憶體中的緩衝 imageView.setImageBitmap(bitmap); GiantLog.d(TAG, "load image data form memory"); } 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 Bitmap getBitmap(String url) { File f = fileCache.getFile(url); //從sd卡 Bitmap b = decodeFile(f); if(b!=null) { GiantLog.d(TAG,"load image from sdcard"); return b; } //從網路 try { Bitmap bitmap = null; URL imageUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection(); conn.setConnectTimeout(30000); conn.setReadTimeout(30000); conn.setInstanceFollowRedirects(true); InputStream is = conn.getInputStream(); OutputStream os = new FileOutputStream(f); Utils.CopyStream(is, os); os.close(); bitmap = decodeFile(f); GiantLog.d(TAG,"load image from network" ); return bitmap; } catch (Exception ex){ ex.printStackTrace(); return null; } } //解碼映像用來減少記憶體消耗 private Bitmap decodeFile(File f){ try { //解碼映像大小 BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(new FileInputStream(f),null,o); //找到正確的刻度值,它應該是2的冪。 final int REQUIRED_SIZE = 70; 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; } BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 1; return BitmapFactory.decodeStream(new FileInputStream(f), null, options); } catch (FileNotFoundException e) { GiantLog.d(TAG,"exception:" + e); } return null; } private class PhotoToLoad { public String url; public ImageView imageView; public PhotoToLoad(String u, ImageView i){ url = u; imageView=i; } } class PhotosLoader implements Runnable { PhotoToLoad photoToLoad; PhotosLoader(PhotoToLoad photoToLoad){ this.photoToLoad=photoToLoad; } @Override public void run() { if(imageViewReused(photoToLoad)) return; Bitmap bmp = getBitmap(photoToLoad.url); memoryCache.put(photoToLoad.url, bmp); if(imageViewReused(photoToLoad)) return; BitmapDisplayer bitmapDisplayer = new BitmapDisplayer(bmp, photoToLoad); Activity activity =(Activity)photoToLoad.imageView.getContext(); activity.runOnUiThread(bitmapDisplayer); } } boolean imageViewReused(PhotoToLoad photoToLoad){ String tag = imageViews.get(photoToLoad.imageView); return tag == null || !tag.equals(photoToLoad.url); } //用於顯示位元影像在UI線程 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); } } /** * 清除所有的緩衝(包括記憶體和SD卡) */ public void clearCache() { memoryCache.clear(); fileCache.clear(); } /** * 只清除記憶體中的緩衝 */ public void clearMemoryCache(){ memoryCache.clear(); } }
具體使用很簡單:
ImageLoader imageLoader = new ImageLoader(Activity.this) ;
imageLoader.DisplayImageWithNodefault(pic_url,imageVew,false); //不需要顯示預設圖片
全部源碼下載連結:
http://download.csdn.net/detail/nanzhiwen666/5955391