2019独角兽企业重金招聘Python工程师标准>>>
这次的内容又是很简单 异步加载图片大家可能做的也很多了 通过阅读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);