Universal-Image-Loader(UIL)这个开源库,相信大家也都熟悉,应该算是Android图片加载这块的集大成者了吧。基本上该有的功能都有了,关于UIL源码的分析,有兴趣的同学可以点击这里。
UIL将各个功能模块都以接口的形式抽象出来,这样大家可以很方便的进行扩展,同时各个功能模块也都提供了默认的实现,可以满足大部分的需求。优秀的框架的扩展性,真的很赞。
我们项目中一直使用是UIL,最近项目有个需求,对UIL中在Disk中的缓存进行加密,不希望通过文件管理器能查看这些图片。
加解密算法不是重点,大家根据自己的需求,选择合适的算法即可(我在项目中选择了简单的AES加密),重点是怎么在UIL中进行拓展。
采用的方案是:
1.自定义DiskCache,存入Disk时,将图片文件加密,从Disk取出的时候,将加密后的缓存图片文件解密。
2.自定义ImageLoadingListener,当图片显示完成后,删除解密后的文件。
首先是自定义DiskCache
/**
*
* 实现了加密解密的DiskCache<br/>
* 继承自BaseDiscCache,重点实现了get方法和两个重载的save方法
*
* @author grumoon
*
*/
public class SecureDiscCache extends BaseDiscCache {
private static final String TEMP_IMAGE_POSTFIX = ".tmp";
private static final String TAG = "SecureDiscCache";
private FileSecUtil fileSecUtil = null;
public SecureDiscCache(File cacheDir) {
super(cacheDir);
this.fileSecUtil = new FileSecUtil();
}
public SecureDiscCache(File cacheDir, File reserveCacheDir) {
super(cacheDir, reserveCacheDir);
this.fileSecUtil = new FileSecUtil();
}
public SecureDiscCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
super(cacheDir, reserveCacheDir, fileNameGenerator);
this.fileSecUtil = new FileSecUtil();
}
/**
* 根据imageUri获取Disk中缓存文件,将其解密并返回。
*/
@Override
public File get(String imageUri) {
Log.v(TAG, "get->" + imageUri);
File sourceFile = getFile(imageUri);
File imageFile = null;
try {
// 解密
imageFile = fileSecUtil.decrypt(sourceFile, sourceFile.getAbsolutePath() + FileSecUtil.DECRYPT_TEMP_FILE_TYPE, FileSecUtil.SEC_KEY);
} catch (Exception e) {
imageFile = null;
}
return imageFile;
}
/**
* 将图片流imageStream写入到Disk中,并做加密处理。
*/
@Override
public boolean save(String imageUri, InputStream imageStream, CopyListener listener) throws IOException {
Log.v(TAG, "save with stream->" + imageUri);
File imageFile = getFile(imageUri);
File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
boolean loaded = false;
try {
OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
try {
loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
} finally {
IoUtils.closeSilently(os);
}
} finally {
IoUtils.closeSilently(imageStream);
if (loaded) {
try {
// 加密
fileSecUtil.encrypt(tmpFile, imageFile.getAbsolutePath(), FileSecUtil.SEC_KEY);
} catch (Exception e) {
loaded = false;
}
}
tmpFile.delete();
}
Log.v(TAG, "save with stream->" + loaded);
return loaded;
}
/**
* 将bitmap对象写入到Disk中,并做加密处理。
*/
@Override
public boolean save(String imageUri, Bitmap bitmap) throws IOException {
Log.v(TAG, "save with bitmap->" + imageUri);
File imageFile = getFile(imageUri);
File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
boolean savedSuccessfully = false;
try {
savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
} finally {
IoUtils.closeSilently(os);
if (savedSuccessfully) {
try {
// 加密
fileSecUtil.encrypt(tmpFile, imageFile.getAbsolutePath(), FileSecUtil.SEC_KEY);
} catch (Exception e) {
savedSuccessfully = false;
}
}
tmpFile.delete();
}
bitmap.recycle();
Log.v(TAG, "save with bitmap->" + savedSuccessfully);
return savedSuccessfully;
}
/**
* 获取加密的文件绝对地址
*
* @param imageUri
* @return
*/
public String getFilePath(String imageUri) {
return getFile(imageUri).getAbsolutePath();
}
}
继承自BaseDiskCache,BaseDiskCache是个抽象类,已基本实现所有功能
我们这里重写了两个save方法,实现了文件加密
重写get方法,实现了文件的解密
这样在缓存的中文件就是加密状态了。
然后是自定义ImageLoadingListener
/**
* 配合SecureDiscCache完成加解密功能 <br/>
* 用于在显示完图片以后,删除未加密文件
*
* @author grumoon
*
*/
public class SecureImageLoadingListener implements ImageLoadingListener {
@Override
public void onLoadingStarted(String imageUri, View view) {
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
deleteImageFile(imageUri);
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
deleteImageFile(imageUri);
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
deleteImageFile(imageUri);
}
public void deleteImageFile(String imageUri) {
try {
ImageLoader imageLoader = ImageLoader.getInstance();
DiskCache diskCache = imageLoader.getDiskCache();
if (diskCache instanceof SecureDiscCache) {
SecureDiscCache sdc = (SecureDiscCache) diskCache;
new File(sdc.getFilePath(imageUri) + FileSecUtil.DECRYPT_TEMP_FILE_TYPE).delete();
}
} catch (Exception e) {
}
}
}
这个类的主要目的,实在解密文件后,Disk中存在一个未加密的文件,我们需要在显示出图片或者显示失败后删掉他。
初始化过程:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.diskCache(new SecureDiscCache(cacheDir,cacheDir,new Md5FileNameGenerator()))
...其它配置
.build();
ImageLoader.getInstance().init(config);
ImageLoader.getInstance().displayImage(UrlUtil.preUrl(uri), imageView,
ImageLoaderUtil.getDisplayImageOptions(),
new SecureImageLoadingListener());
1.在调用SecureDiskCache的get方法(解密文件,产生未加密的文件)到 显示图片完成删除图片之前的时间内(时间很短),Disk中会存在未加密的文件,希望能得到改进。
2.SecureDiskCache中的两个save方法,基本上都是复制了BaseDiskCache中的方法,将流或者bitmap存到disk中,再通过加密工具对文件进行处理。最好是能够直接对流或者bitmap进行处理,直接加密生成文件,免去中间过程和提高安全性。