朋友你是否有过这样的经历,当你刷空间,刷微博的时候,有的时候那些图片加载要很长时间,有的却直接可以看到无需慢慢等待,尤其是那些你已经刷过的内容,即使你处于断网的状态下也能看到,往往这种客户体验相对而言比较好,但是有的时候我们清理了手机的一些垃圾后,就不一样了,我们还得从新刷出来,下面我就要说说这个原理了。
这所有的实现都是通过异步加载产生的,原理就是在我们运行软件时,我们需要将一些图片显示出来,那这些图片要从哪里获取呢,我们可以通过以下的方式获取,先看一张流程图:
首先我们先从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);方法即可。