????? 朋友你是否有过这样的经历,当你刷空间,刷微博的时候,有的时候那些图片加载要很长时间,有的却直接可以看到无需慢慢等待,尤其是那些你已经刷过的内容,即使你处于断网的状态下也能看到,往往这种客户体验相对而言比较好,但是有的时候我们清理了手机的一些垃圾后,就不一样了,我们还得从新刷出来,下面我就要说说这个原理了。
????? 这所有的实现都是通过异步加载产生的,原理就是在我们运行软件时,我们需要将一些图片显示出来,那这些图片要从哪里获取呢,我们可以通过以下的方式获取,先看一张流程图:
?
?????? 首先我们先从map缓存中获取图片,map就是我们先前已经下载好的图片的集合,如果没有,我们查找图片文件,看是否有相应的文件,如果没有我们就开辟一个线程下载图片,等图片下载完成后,我们就将图片的url和图片放到map集合中。下一次再次刷新时,我们就直接从文件中读取已经存在的图片。到这里你应该知道如何更省流量得刷空间了吧,但是我们要实现这种功能额。下面我讲解一下核心代码:
?????? 首先我们创建这样一个类LazyImageLoader,类里面有下面的方法
public Bitmap get(String url,ImageLoaderCallback callback) { Bitmap bitmap = null; //先看map集合众是否有这个url的图片 if(imgManger.contains(url)) { //直接获取 bitmap = imgManger.getFromCache(url); return bitmap; } else { callbackManager.put(url, callback); //开启线程下载 startDownLoadTread(url); } //先返回一个默认的图片 return getDefaultImag(R.drawable.user_head); }
?
???? ?首先我们先从map中查看是否有这个url的图片,如果有直接返回,如果没有开启线程下载,同时先返回一个固定的默认图片接下来我们就要看一下ImgManger中关于图片文件获取的主要方法了:
????? 首先我们新建一个map如下Map<String, SoftReference<Bitmap>>imgCache;至于SoftReference是什么,我就不说了,在这里主要是提高运行速度。
????? 首先我们先从上面定义的map中查找,代码如下:
public Bitmap getFromMapCache(String url) { Bitmap bitmap = null; SoftReference<Bitmap> ref = null; synchronized (this) { ref = imgCache.get(url); } if(null != ref) { bitmap = ref.get(); } return bitmap; }
?这里我们要注意防止线程同步的出现加关键词synchronized;
?????? 如果没有,我们再从查找文件,看是否存在,这里我遇到了一个问题,同时我也明白了一件经常看到的事,显然我们要下载的图片的url是如http://www.xxx/xxx.png这样的格式,那我们将下载的文件命名保存呢?如果我们直接以url保存,那最后是读取和保存的,如果我们重命名,那么首先我们要怎样才能确定保存的是独一无二的呢,其次读取时又要按先前对文件命名的规则对URL解析然后再获取图片,这样一来将会影响读取的速度,那解决办法是有的,通过使用加密算法对url直接加密生成一个独一无二的摘要即可,在这里我直接通过Md5进行加密,到这里你应该明白有时候我们看到的文件的名字是23d4c0e84e1e642088d184807e56e324,115d7b4284479089001c7962b2b906b1之类的吧。下面我们继续写代码:
public Bitmap getFromFile(String url) { String fileName = this.getMd5(url); FileInputStream is=null; try { is=context.openFileInput(fileName); return BitmapFactory.decodeStream(is); } catch (FileNotFoundException e) { e.printStackTrace(); return null; } finally { if(null != is) { try{is.close();}catch(Exception ex){}; } }
??? 上面是直接从文件中查找图片,如果此时还没有,那么我们只有开启线程,通过线程来下载我们所需的图片,再将图片写到文件在写文件的时候文件名是也需要通过md5加密的。线程中主要的代码如下:
public Bitmap downloadImg(String urlStr) throws HttpException { try { URL url = new URL(urlStr); HttpURLConnection connection =(HttpURLConnection) url.openConnection(); String fileName=writerToFile(urlStr,connection.getInputStream()); return BitmapFactory.decodeFile(fileName); } catch (IOException e) { e.printStackTrace(); } return null; }
?上面是通过http从网络中获取数据,其中的writerToFile方法如下:
public String writerToFile(String fileName, InputStream is) { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(is); bos = new BufferedOutputStream(context.openFileOutput(MD5Utils.md5(fileName), Context.MODE_PRIVATE)); byte[] buffer = new byte[1024]; int length; while((length = bis.read(buffer)) != -1) { bos.write(buffer, 0, length); } } catch (Exception e) { } finally { try { if(null != bis) { bis.close(); } if(null != bos) { bos.flush(); bos.close(); } } catch (IOException e) { e.printStackTrace(); } } return context.getFilesDir() + "/" + MD5Utils.md5(fileName); }
?主要是快速读写文件的操作,挺简单的,不懂直接百度,最后当我们的文件写好了,再通过回调的方法
将图片显示出来就行了。下面是我写的小项目的测试,效果还是挺好的,
异步加载的源码我放在下面,稍微改一下
可以直接用,直接调用类SimpleImageLoader 中的showImg(ImageView view,String url);方法即可。
?