上一次分析了andbase框架的AbActivity 安卓andbase框架源码解读(一),不知道有没有让大家对这个框架产生点兴趣,这次我要分析的是一个平常比较常用也比较简单但是还挺烦人的知识点:下载图片,当然现在有很多框架比如ImageLoader,Volley里的专门用来从网上加载数据的NetworkImageView,性能还都是不错的,以后博主也一定会把这些源码都分析一下的,毕竟reading the fucking source code还是可以让我们加深对知识的理解,提高自己的水平的。那么,我就开始今天的分析了
andbase框架下载图片使用的AsyncTask,LruCache,在本次sdcard上的缓存原作者也是自己实现的,其实也可以用谷歌的DiskLruCache实现可能更方便一点,当然这么写可能是因为原作者太牛了。。。
首先图片下载当然是要单独开线程的了,这里用到的是AbImageDownloadTask,继承自AsyncTask,
public class AbImageDownloadTask extends AsyncTask<AbImageDownloadItem, Integer, AbImageDownloadItem>
我们都知道这个类处理后台任务的方法都是在doInBackground中的,那么就让我们看一下这个方法,
/**
* 这里的第一个参数对应AsyncTask中的第一个参数
* 这里的String返回值对应AsyncTask的第三个参数
* 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
* 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
*/
@Override
protected AbImageDownloadItem doInBackground(AbImageDownloadItem... items) {
AbImageDownloadItem item = items[0];
//检查图片路径
String url = item.imageUrl;
if(AbStrUtil.isEmpty(url)){
if(D)Log.d(TAG, "图片URL为空,请先判断");
}else{
url = url.trim();
}
String cacheKey = AbImageCache.getCacheKey(url, item.width, item.height, item.type);
item.bitmap = AbImageCache.getBitmapFromCache(cacheKey);
if(item.bitmap == null){
//开始下载
item.bitmap = AbFileUtil.getBitmapFromSDCache(item.imageUrl,item.type,item.width,item.height);
//缓存图片路径
AbImageCache.addBitmapToCache(cacheKey,item.bitmap);
//需要执行回调来显示图片
if (item.listener != null) {
Message msg = handler.obtainMessage();
msg.obj = item;
handler.sendMessage(msg);
}
}else{
if(D) Log.d(TAG, "从内存缓存中得到图片:"+cacheKey+","+item.bitmap);
if (item.listener != null) {
Message msg = handler.obtainMessage();
msg.obj = item;
handler.sendMessage(msg);
}
}
return item;
}
首先要是用这个类,我们要在它的execute()方法中传进去一个类型为AbImageDownloadItem的参数,这个类就相当于是我们平常用的实体类,
public class AbImageDownloadItem {
/** 需要下载的图片的互联网地址. */
public String imageUrl;
/** 显示的图片的宽. */
public int width;
/** 显示的图片的高. */
public int height;
/** 图片的处理类型(剪切或者缩放到指定大小,参考AbConstant类). */
public int type;
/** 下载完成的到的Bitmap对象. */
public Bitmap bitmap;
/** 下载完成的回调接口. */
public AbImageDownloadListener listener;
}
这里面还有相对应的get和set方法,我就不贴了,属性的作用在注释里写的也很清楚了(不是本人写的,是原作者的功劳,赞一个),我们可以看到在后台处理的方法里,首先判断传进去的参数是不是空,是的话直接返回错误信息,否则从缓存中找是不是已经加载过这张图片了,是的话通过Handler发布信息,进行异步处理(这里估计是原作者写重复了,因为我们一会会看到handler的处理和afterExecute方法里执行的动作是一样的,这样写的话不是会执行两次更新函数吗),如果现在内存中没有缓存的话,那就要看看sdcard上有没有了,用的是getBitmapFromSDCache这个方法
/**
* 描述:通过文件的网络地址从SD卡中读取图片,如果SD中没有则自动下载并保存.
* @param url 文件的网络地址
* @param type 图片的处理类型(剪切或者缩放到指定大小,参考AbConstant类)
* 如果设置为原图,则后边参数无效,得到原图
* @param width 新图片的宽
* @param height 新图片的高
* @return Bitmap 新图片
*/
public static Bitmap getBitmapFromSDCache(String url,int type,int width, int height){
Bitmap bitmap = null;
try {
if(AbStrUtil.isEmpty(url)){
return null;
}
//SD卡不存在 或者剩余空间不足了就不缓存到SD卡了
if(!isCanUseSD() || freeSdSpaceNeededToCache < freeSpaceOnSD()){
bitmap = getBitmapFormURL(url,type,width,height);
return bitmap;
}
if(type != AbConstant.ORIGINALIMG && ( width<=0 || height<=0)){
throw new IllegalArgumentException("缩放和裁剪图片的宽高设置不能小于0");
}
//缓存的key,也是文件名
String key = AbImageCache.getCacheKey(url, width, height, type);
//文件是否存在
File path = Environment.getExternalStorageDirectory();
File fileDirectory = new File(path.getAbsolutePath() + downPathImageDir);
//获取后缀
String suffix = getSuffixFromNetUrl(url);
//缓存的图片文件名
String fileName = key+suffix;
File file = new File(fileDirectory,fileName);
//检查文件缓存中是否存在文件
File fileCache = AbFileCache.getFileFromCache(fileName);
if(fileCache == null){
String downFilePath = downFileToSD(url,file.getName());
if(downFilePath != null){
//下载成功后存入缓存
AbFileCache.addFileToCache(fileName, file);
//获取
return getBitmapFromSD(file,type,width,height);
}else{
return null;
}
}else{
bitmap = getBitmapFromSD(file,type,width,height);
if(D) Log.d(TAG, "从SD缓存中得到图片:"+key+","+bitmap);
//获取
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
过程类似上面,先看sdcard上有没有,有的话直接加载,没有的话从网上加载,然后加入缓存,这应该还是我们都能想到的思路,getBitmapFormURL其实最终就是通过UrlConnection打开输入流读取图片,大家肯定很清楚了。鉴于很多地方注释已经写得很详细了,我就不过多地解释了,希望这次的分析(其实也不叫分析,就是把我认为好的代码拿出来分享,)能给大家带来一点帮助,求各路大神多加点评,多多指导