笑谈风云,一语定乾坤。大家好,我是皖江。今天,我继续和大家分享我的Android框架学习经验之路。上次我熟悉了Android自带的二级内存缓存机制LruCache的使用,这次来学习由第三方JakeWharton的git开源项目DiskLruCache.
这是项目的git地址:https://github.com/JakeWharton/DiskLruCache
jar包下载链接:https://search.maven.org/remote_content?g=com.jakewharton&a=disklrucache&v=LATEST
依赖注入:
<dependency>
<groupId>com.jakewharton</groupId>
<artifactId>disklrucache</artifactId>
<version>2.0.2</version>
</dependency>
gradle
compile 'com.jakewharton:disklrucache:2.0.2'
相关介绍:
DiskLruCache是在文件系统上使用有限空间的高速缓存。每个缓存条目都有一个字符串键和固定数量的值。每个键必须匹配正则表达式[a-z0-9_-]{1,120}。值是二进制,可以通过流或者文件访问。值的长度必须大于0,不可为空。
缓存数据的目录必须是单独的,不提倡多线程操作缓存目录,线程上是不安全的。缓存可以设置最大值,当存储的字节数超过了最大值时,缓存将删除较先进入缓存的文件。
当调用edit来创建或者更新条目的值时,只能用一个edit实例来进行操作。如果当前值不可操作时,该次编辑返回为null。
当编辑条目时,不必为每个值提供数据,其值默认为之前的值。每个编辑必须调用Editor.commit或者Editor.abort进行匹配。
我是用AS来编译项目的,这次是通过JAR包的形式引入。
首先,设置缓存所在路径:
/** * * @param context * @param dataType 区分存储文件类型 * 当内存卡存在时:存储路径为/sdcard/Android/data/${包名}/cache * 当内存卡不存在是:存储路径为/data/data/${包名}/cache * @return */ public static File getDiskLruCacheDir(Context context, String dataType) { String path; File cacheFile = null; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {//判断内存卡是否挂载 path=context.getExternalCacheDir().getPath(); } else { path=context.getCacheDir().getPath(); } cacheFile=new File(path+File.separator+dataType); return cacheFile; }
然后获取APP版本号
/** * 获取APP版本号 * @param ctx * @return */ public static int getAppVersionValue(Context ctx){ int version = 1; try{ String packageName = ctx.getPackageName(); PackageManager pm = ctx.getPackageManager(); PackageInfo info = pm.getPackageInfo(packageName,0); version = info.versionCode; }catch (PackageManager.NameNotFoundException e){ e.printStackTrace(); } return version; }
再封装一个将URL路径转换为MD5串的方法,因为DiskLruCache的key只认MD5
/** * 字符串MD5加密 * @param str * @return */ public static String getStringMD5(String str){ String md5Str = null; try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(str.getBytes()); byte bytes[] = messageDigest.digest(); if (bytes == null || bytes.length == 0) { return md5Str; } StringBuffer stringBuffer = new StringBuffer(); for(int i=0;i<bytes.length;i++){ stringBuffer.append(Integer.toHexString(0xFF & bytes[i]));//byte与OxFF进行与运算,然后通过toHexString获取16进制字符串 } md5Str = stringBuffer.toString(); return md5Str; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return md5Str; }接下来我再写一个简单的网络请求图片方法,该方法会将返回的流转换成Bitmap进行返回
/** * 从网络下载图片 * @param imageUrl * @return */ public static Bitmap getBitmapFromNet(String imageUrl){ Bitmap bitmap = null; URL url=null; HttpURLConnection httpURLConnection=null; InputStream inputStream=null; try { url=new URL(imageUrl); Log.e("test","网络请求路径:"+imageUrl); httpURLConnection=(HttpURLConnection) url.openConnection(); httpURLConnection.setConnectTimeout(5*1000);//设置连接超时时长 httpURLConnection.setReadTimeout(10*1000);//设置读取超时时长 httpURLConnection.setDoInput(true); httpURLConnection.setDoOutput(true); Log.e("test","网络请求返回码:"+httpURLConnection.getResponseCode()+""); if (httpURLConnection.getResponseCode()==200) { inputStream=httpURLConnection.getInputStream(); bitmap = BitmapFactory.decodeStream(inputStream); } else { Log.e("test","下载失败"); } } catch (Exception e) { Log.e("test","下载失败"); }finally{ try { if (inputStream!=null) { inputStream.close(); } if (httpURLConnection!=null) { httpURLConnection.disconnect(); } } catch (Exception e) { Log.e("test","关闭流失败"); } } return bitmap; }一开始我的思路是这样的,先初始化DiskLruCache,然后拿着图片的URL路径去缓存中找图片,如果没有图片则上网下载。下载成功后返回Bitmap,并将Bitmap存入缓存中,且显示在页面。但是将Bitmap存入缓存中的时候,遇到了要将Bitmap压缩转换成InputStream流的麻烦。转换方法如下:
/** * Bitmap 转 InputStream * @param bitmap * @return */ public static InputStream bitmapToIS(Bitmap bitmap){ InputStream is = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG,100,baos);// 压缩图片 PNG会最大程度不让图片失真 is = new ByteArrayInputStream(baos.toByteArray()); return is; }这种方法我通过实验得知,唯一的缺憾就是转换PNG图片比较耗时,转换JPG的图片失真严重。真的不能要啊,这样的话,我这思路就行不通了啊。大王,臣妾做不到啊!!后来我想了一下,我在下载的时候就已经拿到流了,何不将流直接写入缓存中,岂不美哉。中间也少了不必要的步骤。于是就有了下面这个方法:
/** * 从网络获取图片且保存至SD卡中的缓存 */ public static boolean getBitmapFromNetWorkAndSaveToDiskLruCache(String imageUrl,OutputStream outputStream){//直接将DiskLruCache需要写的流对象传进来 boolean isSuccessfull=false; URL url=null; HttpURLConnection httpURLConnection=null; BufferedOutputStream bufferedOutputStream=null; InputStream inputStream=null; BufferedInputStream bufferedInputStream=null; try { url=new URL(imageUrl); Log.e("test","网络请求路径:"+imageUrl); httpURLConnection=(HttpURLConnection) url.openConnection(); httpURLConnection.setConnectTimeout(5*1000); httpURLConnection.setReadTimeout(10*1000); httpURLConnection.setDoInput(true); httpURLConnection.setDoOutput(true); Log.e("test","网络请求返回码:"+httpURLConnection.getResponseCode()+""); if (httpURLConnection.getResponseCode()==200) { bufferedOutputStream=new BufferedOutputStream(outputStream); inputStream=httpURLConnection.getInputStream(); bufferedInputStream=new BufferedInputStream(inputStream); int len=0; byte [] buffer=new byte[1024]; while((len=bufferedInputStream.read(buffer))!=-1){//将inputStream流写入到bufferedOutputStream中 bufferedOutputStream.write(buffer, 0, len); bufferedOutputStream.flush(); } isSuccessfull=true; } else { isSuccessfull=false; System.out.println("下载失败"); } } catch (Exception e) { isSuccessfull=false; System.out.println(e+e.toString()); }finally{ try { if (bufferedOutputStream!=null) { bufferedOutputStream.close(); } if (inputStream!=null) { inputStream.close(); } if (bufferedInputStream!=null) { bufferedInputStream.close(); } if (httpURLConnection!=null) { httpURLConnection.disconnect(); } } catch (Exception e) { System.out.println(e+e.toString()); } } return isSuccessfull; }这样的话,图片流在拿到的同时就直接写入缓存了。以下是具体思路流程:
/** * 从网络获取图片并存入缓存中 * @param url */ public void getImageFromNet(final String url){ new Thread(){ @Override public void run() { String key = Utils.getStringMD5(url);//1.生成key try { DiskLruCache.Editor editor = mDiskLruCache.edit(key);//2.获取本次编辑器 if(editor!=null){ OutputStream os = editor.newOutputStream(0); boolean isSuccess = Utils.getBitmapFromNetWorkAndSaveToDiskLruCache(url,os);//3.下载图片并将图片写入缓存 if(isSuccess){//4.写入成功,提交本次写入 editor.commit(); mHandler.sendEmptyMessage(1); }else{//4.写入失败,放弃本次提交 editor.abort(); mHandler.sendEmptyMessage(2); } } } catch (IOException e) { e.printStackTrace(); } } }.start(); }初始化
/** * 初始化缓存 */ public void initDiskLruCache(){ //初始化DiskLruCache File fileCache = Utils.getDiskLruCacheDir(this,"bitmap");//缓存文件夹 if(!fileCache.exists()){ fileCache.mkdirs(); } int versionValue = Utils.getAppVersionValue(this);//版本值 int maxSize = 10*1024*1024;//缓存区大小 try { mDiskLruCache = DiskLruCache.open(fileCache,versionValue,1,maxSize); } catch (IOException e) { e.printStackTrace(); } }
从缓存中读取图片
/** * 从缓存中读取图片 * @param url * @return */ public Bitmap getBitmapFromCache(String url){ String key = Utils.getStringMD5(url);//获取key Bitmap bitmap = null; try { if(!mDiskLruCache.isClosed()){ DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);//获取快照 if(snapshot!=null){ InputStream is = snapshot.getInputStream(0);//从快照中获取流 bitmap = BitmapFactory.decodeStream(is); } } } catch (IOException e) { e.printStackTrace(); } return bitmap; }清除所有缓存
mDiskLruCache.delete();以上就是DiskLruCache的基本用法了,我再将Activit 的代码贴一下:
public class DiskLruCacheActivity extends BaseActivity{
private DiskLruCache mDiskLruCache;
private String[] urls = {
"http://www.baidu.com/img/bdlogo.png",
"http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=http%3A%2F%2Fwww.icosky.com%2Ficon%2Fpng%2FSystem%2FVista%2520System%2FVista%2520%252816%2529.png&thumburl=http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D2282442470%2C974371679%26fm%3D21%26gp%3D0.jpg",
"http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=http%3A%2F%2Fimg.article.pchome.net%2F00%2F40%2F48%2F13%2Fpic_lib%2Fs960x639%2Fstariconrt2s960x639.JPG&thumburl=http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D2571315283%2C182922750%26fm%3D21%26gp%3D0.jpg",
"http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=http%3A%2F%2Fwww.icosky.com%2Ficon%2Fpng%2FSystem%2FFrenzic%2520System%2FHome%2520%2528Alternate%2529.png&thumburl=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1576708873%2C969493934%26fm%3D21%26gp%3D0.jpg"};
@Bind(R.id.iv)
ImageView mImageView;
@Bind(R.id.et_url)
EditText mEtUrl;
@Bind(R.id.btn_get_from_net)
Button mBtnGetFromNet;
@Bind(R.id.btn_get_from_local)
Button mBtnGetFromLocal;
@Bind(R.id.btn_clean)
Button mBtnClean;
@Bind(R.id.btn_change)
Button mBtnChange;
private String url;
private int nowPosition = 0;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
Bitmap bitmap = getBitmapFromCache(url);
mImageView.setImageBitmap(bitmap);
break;
case 2:
break;
}
}
};
@Override
protected void onActivityCreate(Bundle savedInstanceState) {
super.onActivityCreate(savedInstanceState);
setContentView(R.layout.activity_disklrucache);
ButterKnife.bind(this);
}
@Override
protected void initView() {
super.initView();
}
@Override
protected void initListener() {
super.initListener();
mBtnChange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(nowPosition
<?xml version="1.0" encoding="utf-8"?>
以上。