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