/SyncLoaderBitmapDemo

a sync load bitmap int listview demo

Primary LanguageJava

Sync Load Image From Url For Android

A simple Demo to display images from url in Android ListView. Images are being downloaded asynchronously in ThreadPool. Images are being cached on SD card and memory. Make the user experience more fluid

github

Code fragment

public class ImageLoader {

private MemoryCache memoryCache = new MemoryCache();
  private AbstractFileCache fileCache;
  private Map<ImageView, String> imageViews = Collections
		.synchronizedMap(new WeakHashMap<ImageView, String>());
// 线程池
private ExecutorService executorService;

public ImageLoader(Context context) {
	fileCache = new FileCache(context);
	executorService = Executors.newFixedThreadPool(5);
}

// 最主要的方法
public void DisplayImage(String url, ImageView imageView, boolean isLoadOnlyFromCache) {
	imageViews.put(imageView, url);
	// 先从内存缓存中查找

	Bitmap bitmap = memoryCache.get(url);
	if (bitmap != null)
		imageView.setImageBitmap(bitmap);
	else if (!isLoadOnlyFromCache){
		
		// 若没有的话则开启新线程加载图片
		queuePhoto(url, imageView);
	}
}

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);
	
	// 先从文件缓存中查找是否有
	Bitmap b = null;
	if (f != null && f.exists()){
		b = decodeFile(f);
	}
	if (b != null){
		return b;
	}
	// 最后从指定的url中下载图片
	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);
		CopyStream(is, os);
		os.close();
		bitmap = decodeFile(f);
		return bitmap;
	} catch (Exception ex) {
		Log.e("", "getBitmap catch Exception...\nmessage = " + ex.getMessage());
		return null;
	}
}

// decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
private Bitmap decodeFile(File f) {
	try {
		// decode image size
		BitmapFactory.Options o = new BitmapFactory.Options();
		o.inJustDecodeBounds = true;
		BitmapFactory.decodeStream(new FileInputStream(f), null, o);

		// Find the correct scale value. It should be the power of 2.
		final int REQUIRED_SIZE = 100;
		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;
		}

		// decode with inSampleSize
		BitmapFactory.Options o2 = new BitmapFactory.Options();
		o2.inSampleSize = scale;
		return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
	} catch (FileNotFoundException e) {
	}
	return null;
}

// Task for the queue
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 bd = new BitmapDisplayer(bmp, photoToLoad);
		// 更新的操作放在UI线程中
		Activity a = (Activity) photoToLoad.imageView.getContext();
		a.runOnUiThread(bd);
	}
}

/**
 * 防止图片错位
 * 
 * @param photoToLoad
 * @return
 */
boolean imageViewReused(PhotoToLoad photoToLoad) {
	String tag = imageViews.get(photoToLoad.imageView);
	if (tag == null || !tag.equals(photoToLoad.url))
		return true;
	return false;
}

// 用于在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);

	}
}

public void clearCache() {
	memoryCache.clear();
	fileCache.clear();
}

public static void CopyStream(InputStream is, OutputStream os) {
	final int buffer_size = 1024;
	try {
		byte[] bytes = new byte[buffer_size];
		for (;;) {
			int count = is.read(bytes, 0, buffer_size);
			if (count == -1)
				break;
			os.write(bytes, 0, count);
		}
	} catch (Exception ex) {
		Log.e("", "CopyStream catch Exception...");
	}
}
}

Feature

The first load from memory , if you can't get it then open the thread from the SD card or network , where attention to get pictures from the SD card on child thread , otherwise rapid slide screen , then would not smooth , and this is to optimize a . At the same time , in the adapter busy variable indicates the listview whether in the sliding state sliding state obtained only from the memory , if not then no need to reopen the thread to external memory or network access to picture this is to optimize the two . ImageLoader thread using a thread pool , so as to avoid excessive thread frequently create and destroy , some children's shoes every time always new thread to perform which is very undesirable , a little better with the AsyncTask class , in fact, also the internal use the thread pool . Get pictures from the network , first save it to the sd card , and then loaded into memory , the benefits of doing so can be loaded into memory compression processing to reduce pictures to share memory which is optimized three .

Links

csdn bolg : http://blog.csdn.net/geniuseoe2012

Development

If you think this article useful Nepal , please pay attention to me, Your support is my motivation, I will continue to strive to do better