最近项目中碰到了多线程下载图片的问题,我自己写了一个工具类 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