自己动手写android图片异步加载库

尊重他人劳动成果,转载请说明出处:http://blog.csdn.net/bingospunky/article/details/44344085

接触android有半年了,关于图片异步加载,一直只用别人的框架,虽然特别方便,但是始终未见识到图片异步加载的庐山真面目。最近比较悠闲,研究一些高大上的东西。在这篇文章总结一下我对图片异步加载的一些学习心得。

图片加载最重要的无非就是内存和线程。大家都知道关于内存溢出一般的解决方案就是LruCache,在我的这个demo里我只要使用SoftReference实现了一个缓存类,SoftReference已经被遗弃了,这里只为学习。

代码

com.example.qtdemo_oom.Cache<T, U>

/**
 * 由SoftReference实现的内存缓存<br>
 * 此类仅供学习,由于Android 2.3开始,垃圾回收器倾向于回收软引用和弱引用对象。
 * @author qingtian 
 */
public class Cache<T,U> {

	private Hashtable<T, NewSoftReference> hashTable;// 用于Chche内容的存储
	private ReferenceQueue<U> q;// 垃圾Reference的队列

	/**
	 * 继承SoftReference,使得每一个实例都具有可识别的标识。
	 */
	private class NewSoftReference extends SoftReference<U> {
		public T key;
		public NewSoftReference(U em, ReferenceQueue<U> q) {
			super(em, q);
		}
	}

	/**
	 * 构建一个缓存器实例
	 */
	public Cache() {
		hashTable = new Hashtable<T, NewSoftReference>();
		q = new ReferenceQueue<U>();
	}

	/**
	 * 保存一个实例
	 * @param t
	 * @param u
	 */
	public void put(T t, U u) {
		cleanCache();// 清除垃圾引用
		NewSoftReference ref = new NewSoftReference(u, q);
		ref.key = t;
		hashTable.put(t, ref);
	}

	/**
	 * 通过uri获取实例
	 * @param t
	 * @return
	 */
	public U get(T t) {
		U bm = null;
		if (hashTable.containsKey(t)) {
			NewSoftReference ref = (NewSoftReference) hashTable.get(t);
			bm = (U) ref.get();
		}
		return bm;
	}

	/**
	 * 在hashTable里里清除已经被回收了的对象的软引用对象
	 */
	@SuppressWarnings("unchecked")
	private void cleanCache() {
		NewSoftReference ref = null;
		while ((ref = (NewSoftReference) q.poll()) != null) {
			hashTable.remove(ref.key);
		}
	}

	/**
	 * 清除Cache内的全部内容
	 */
	public void clearCache() {
		cleanCache();
		hashTable.clear();
		System.gc();
		System.runFinalization();
	}
}


com.example.qtdemo_oom.ImageLoader

/**
 * 图片加载工具类
 * @author qingtian
 */
public class ImageLoader {
	
	private static final String TAG = "qingtian" ;

	//线程池
	ExecutorService threadPool ;
	//自己实现的缓存类
	Cache<String, Bitmap> cache;
	//也可以用v4包提供的缓存
//	LruCache<String, Bitmap> cache;
	
	public ImageLoader() {
		threadPool = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
		cache = new Cache<String, Bitmap>();
//		int maxMemory = (int) Runtime.getRuntime().maxMemory();
//		cache = new LruCache<String, Bitmap>(maxMemory/8){
//			// 测量Bitmap的大小
//			@Override
//			protected int sizeOf(String key, Bitmap value) {
//				return value.getRowBytes() * value.getHeight();
//			}
//		};
	}

	private int currentThreadNum = 0;
	
	class DownBitmapRunnable implements Runnable{
		ImageView iv;  String uri;
		public DownBitmapRunnable( ImageView iv,  String uri) {
			this.iv = iv;
			this.uri = uri;
		}
		@Override
		public void run() {

			currentThreadNum++;
			
			show();

			Bitmap bm = cache.get(uri);
			if (bm == null) {
				Log.i(TAG, "下载");
				bm = BitmapUtil.getImageBitmap(uri);
			}else{
				Log.i(TAG, "不下载");
			}
			if (bm != null) {
				cache.put(uri, bm);
				final Bitmap fBitmap = bm;
				new Handler(Looper.getMainLooper()).post(new Runnable() {

					@Override
					public void run() {
						iv.setImageBitmap(fBitmap);
					}

				});
			}

			currentThreadNum--;
		}
		
	}

	public void show( ImageView iv,  String uri) {

		threadPool.execute(new DownBitmapRunnable(iv, uri));

	}

	public void show() {
		Log.i(TAG, "currentThreadNum:" + currentThreadNum );
	}
}
在这个类里,使用线程池管理线程,避免创建/取消线程时内存的浪费。第14行代码也提供了LruCache代码,我们也可以用LruCache来实现缓存。代码第54--61行,对于view的操作已经在主线程里了。

com.example.qtdemo_oom.Adapter

public class Adapter extends BaseAdapter {
	public static final String[] uris = new String[] {
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img1b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img2b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img3b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img4b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img5b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img6b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img7b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img8b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img9b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img10b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img11b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img12b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img13b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img14b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img15b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img16b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img17b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img18b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img19b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img20b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img21b.jpg",
			"http://mabinbin.com:8080/AndroidAsyncHttpService/image/img22b.jpg" };

	public LayoutInflater mInflater;
	public ImageLoader loader;

	public Adapter(Context context) {
		mInflater = LayoutInflater.from(context);
		loader = new ImageLoader();
	}

	@Override
	public int getCount() {
		return uris.length;
	}

	@Override
	public Object getItem(int arg0) {
		return uris[arg0];
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		ViewHolder holder = null;
		if (convertView == null) {
			convertView = mInflater.inflate(R.layout.item, null);

			holder = new ViewHolder();
			holder.tv = (TextView) convertView.findViewById(R.id.tv);
			holder.iv = (ImageView) convertView.findViewById(R.id.iv);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		holder.tv.setText((String) getItem(position));

		loader.show(holder.iv, uris[position]);

		return convertView;
	}

	class ViewHolder {
		TextView tv;
		ImageView iv;
	}

}

代码第66行,就可以异步加载图片了。


com.example.qtdemo_oom.util.BitmapUtil

public class BitmapUtil {
	
	public static Bitmap getImageBitmap(String uri) {
		URL imgUrl = null;
		Bitmap bitmap = null;
		InputStream is = null;
		try {
			imgUrl = new URL(uri);
			HttpURLConnection conn = (HttpURLConnection) imgUrl
					.openConnection();
			conn.setDoInput(true);
			conn.connect();
			is = conn.getInputStream();
			bitmap = BitmapFactory.decodeStream(is);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (is != null) {
				try {
					is.close();
					is = null;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return bitmap;
	}
	
}
通过这个简单的demo,我见到了内存缓存的真面目,也学习了LruCache的源码(这里没体现)。当然这是我学习异步图片加载的第一版,希望在后面的版本里写的更成熟。

源码下载

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。