这次的内容又是很简单 异步加载图片大家可能做的也很多了 通过阅读osc的源码 也算是 学习人家的一种架构吧,好歹作者的经验也比我丰富。
其实osc的图片异步加载也是使用了两级结构,即 内存缓存和sd卡缓存。整体思路:显示图片时先从内存缓存中找,如果找到,就直接返回这个bitmap并显示,如果找不到从sd卡找,找到就显示,找不到就从网路获取并保存到内存缓存和sd卡中
时序图如下
那么就来分析一下流程吧这里只提及重要的步骤了,其他的步骤请参考代码
1.在Adapter等地方设置这个显示这个图片,其实是调用了BitmapManager的loadBitmap函数
bmpManager.loadBitmap(imgSmall, listItemView.image, BitmapFactory.decodeResource(context.getResources(), R.drawable.image_loading));
2.loadBitmap函数代码
public void loadBitmap(String url, ImageView imageView, Bitmap defaultBmp, int width, int height) { imageViews.put(imageView, url); Bitmap bitmap = getBitmapFromCache(url); if (bitmap != null) { //显示缓存图片 imageView.setImageBitmap(bitmap); } else { //加载SD卡中的图片缓存 String filename = FileUtils.getFileName(url); String filepath = imageView.getContext().getFilesDir() + File.separator + filename; File file = new File(filepath); if(file.exists()){ //显示SD卡中的图片缓存 Bitmap bmp = ImageUtils.getBitmap(imageView.getContext(), filename); imageView.setImageBitmap(bmp); }else{ //线程加载网络图片 imageView.setImageBitmap(defaultBmp); queueJob(url, imageView, width, height); } } }
这里先分析从内存Cache中获取Bitmap的实现:
在分析获取缓存之前有个imageViews成员变量需要解释一下
imageViews.put(imageView, url);
这个 imageViews.put(imageView, url); 的定义是
Map<ImageView, String> imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());这里的Collections.synchronizedMap 是为了让HashMap可以用于多线程的环境
而 WeakHashMap当某个键不再正常使用时,将自动移除其条目。这个imageViews是为了在线程池中把imageview和url(其实就是从网络取回来的bitmap)一一对应 稍后还会看到。
4.getBitmpaFromCache函数
public Bitmap getBitmapFromCache(String url) { Bitmap bitmap = null; if (cache.containsKey(url)) { bitmap = cache.get(url).get(); } return bitmap; }那么,这个Cache是个什么东西呢?
private static HashMap<String, SoftReference<Bitmap>> cache;
cache = new HashMap<String, SoftReference<Bitmap>>();
原来是一个HashMap 其中他的键值String是那个需要显示的Bitmap的url 我以前做的项目是把这个url md5了 市面上很多应用比如京东也是进行的md5再放入缓存
至于SofrReference请自行google。
继续看代码,。如果从缓存中取到了bitmap,就直接显示,若没有就执行
Bitmap bmp = ImageUtils.getBitmap(imageView.getContext(), filename);从sd卡取,如果sd卡没有这个文件,那直接从网路下载了 在此之前 还让imageview显示了一个默认图片
7.queueJob()
public void queueJob(final String url, final ImageView imageView, final int width, final int height) { /* Create handler in UI thread. */ final Handler handler = new Handler() { public void handleMessage(Message msg) { String tag = imageViews.get(imageView); if (tag != null && tag.equals(url)) { if (msg.obj != null) { imageView.setImageBitmap((Bitmap) msg.obj); try { //向SD卡中写入图片缓存 ImageUtils.saveImage(imageView.getContext(), FileUtils.getFileName(url), (Bitmap) msg.obj); } catch (IOException e) { e.printStackTrace(); } } } } }; pool.execute(new Runnable() { public void run() { Message message = Message.obtain(); message.obj = downloadBitmap(url, width, height); handler.sendMessage(message); } }); }
这里又是一个handler+thread了
有同学会说,这里木有thread,其实那个post就是一个线程池了
看定义
private static ExecutorService pool;
pool = Executors.newFixedThreadPool(5); //固定线程池
就是把downloadBitmap的任务放到线程池中执行了,等执行完了就发消息给handler
handler会使用从imageviews中保存的url进行匹配,如果消息写到的url是imageview需要显示的那个,就显示到imageview上
注意在从网路download取回bitmap之后会把bitmap放到cache'中 这句话在downloadBitmap()函数中
cache.put(url, new SoftReference<Bitmap>(bitmap));13.最后异步就是把获取到的bitmap保存到sd卡了
//向SD卡中写入图片缓存 ImageUtils.saveImage(imageView.getContext(), FileUtils.getFileName(url), (Bitmap) msg.obj);