尊重他人劳动成果,转载请说明出处: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();
}
}
/**
* 图片加载工具类
* @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的源码(这里没体现)。当然这是我学习异步图片加载的第一版,希望在后面的版本里写的更成熟。