Android OkHttp与物理存储介质缓存:DiskLruCache(2)

本文在附录文章8,9的基础之上,把Android OkHttp与DiskLruCache相结合,综合此两项技术,实现基于OkHttp的物理存储介质缓存DiskLruCache。

用一个完整的例子加以说明。该例子的代码要实现这样的过程:代码启动后,要往一个ImageView里面加载一张网络图片,首先检查DiskLruCache是否已经存在该图片的缓存,如果存在,则直接复用缓存,如果不存在则使用OkHttp把图片异步从网络加载,当OkHttp异步加载网络图片成功后,要做两件事情:

一, 毫无疑问,要把该图片设置到目标ImageView里面。代码启动后首先要检查本地的DiskLruCache物理存储介质上是否已经有特定图片的缓存,如果有,则直接复用,不再浪费网络资源重复加载。

二,把该图片的数据写入DiskLruCache缓存中,为以后的缓存使用。此情况是当DiskLruCache不存在特定资源(本例是图片)缓存时候,要从网络加载。我使用OkHttp网络驱动加载,当OkHttp加载图片成功后,一方面要把图片设置到ImageView,另外一方面要把图片缓存到DiskLruCache以备后续使用。

完整代码:

package zhangphil.demo;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Callback; import com.jakewharton.disklrucache.DiskLruCache; public class MainActivity extends AppCompatActivity { private String TAG = "zhangphil_tag"; private String UNIQUENAME = "zhangphil_cache"; private DiskLruCache mDiskLruCache = null; //缓存大小
private int DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //初始化DiskLruCache
makeDiskLruCache(); //在布局里面放一个ImageView,放网络请求后的图片
final ImageView image = (ImageView) findViewById(R.id.imageView); //我的博客头像
String image_url = "http://avatar.csdn.net/9/7/A/1_zhangphil.jpg"; Bitmap bmp = readBitmapFromDiskLruCache(image_url); //首先检查DiskLruCache是否已经缓存了特定资源,如果有则直接复用。
//如果没有则从网路加载。
if (bmp != null) {
image.setImageBitmap(bmp);
} else {
downloadBitmapFromNetwork(image, image_url);
}
} //从DiskLruCache中读取缓存
private Bitmap readBitmapFromDiskLruCache(String url) {
DiskLruCache.Snapshot snapShot = null;
try {
//把url转换成一个md5字符串,然后以这个md5字符串作为key
String key = urlToKey(url); snapShot = mDiskLruCache.get(key);
} catch (Exception e) {
e.printStackTrace();
} if (snapShot != null) {
Log.d(TAG, "发现缓存:" + url);
InputStream is = snapShot.getInputStream(0);
Bitmap bitmap = BitmapFactory.decodeStream(is);
Log.d(TAG, "从缓存中读取Bitmap."); return bitmap;
} else
return null;
} //把byte字节写入缓存DiskLruCache
private void writeToDiskLruCache(String url, byte[] buf) throws Exception {
Log.d(TAG, url + " : 开始写入缓存..."); //DiskLruCache缓存需要一个key,我先把url转换成md5字符串,
//然后以md5字符串作为key键
String key = urlToKey(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key); OutputStream os = editor.newOutputStream(0);
os.write(buf);
os.flush();
editor.commit(); mDiskLruCache.flush(); Log.d(TAG, url + " : 写入缓存完成.");
} private void makeDiskLruCache() {
try {
File cacheDir = getDiskCacheDir(this, UNIQUENAME); if (!cacheDir.exists()) {
Log.d(TAG, "缓存目录不存在,创建之...");
cacheDir.mkdirs();
} else
Log.d(TAG, "缓存目录已存在,不需创建."); //第二个参数我选取APP的版本code。DiskLruCache如果发现第二个参数version不同则销毁缓存
//第三个参数为1,在写缓存的流时候,newOutputStream(0),0为索引,类似数组的下标
mDiskLruCache = DiskLruCache.open(cacheDir, getVersionCode(this), 1, DISK_CACHE_MAX_SIZE);
} catch (Exception e) {
e.printStackTrace();
}
} private void downloadBitmapFromNetwork(final ImageView image, final String image_url) {
Log.d(TAG, "从网络中加载图片资源 ... @ " + image_url); //初始化OkHttpClient
final OkHttpClient client = new OkHttpClient(); //创建OkHttpClient针对某个url的数据请求
Request request = new Request.Builder().url(image_url).build(); Call call = client.newCall(request); //请求加入队列
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//此处处理请求失败的业务逻辑
} @Override
public void onResponse(Call call, Response response) throws IOException {
//如果response响应成功则继续,否则返回
if (!response.isSuccessful())
return; //我写的这个例子是请求一个图片
//response的body是图片的byte字节
byte[] bytes = response.body().bytes(); //已经获得图片数据,记到要写入硬盘缓存
//出于性能考虑,此处可以放到后台或者放到一个线程里面处理
//简单期间,我就在这儿直接写缓存了。
try {
writeToDiskLruCache(image_url, bytes);
} catch (Exception e) {
e.printStackTrace();
} //把byte字节组装成图片
final Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); //回调是运行在非ui主线程,
//数据请求成功后,在主线程中更新
runOnUiThread(new Runnable() {
@Override
public void run() {
//网络图片请求成功,更新到主线程的ImageView
image.setImageBitmap(bmp);
}
});
}
});
} /*
*
* 当SD卡存在或者SD卡不可被移除的时候,就调用getExternalCacheDir()方法来获取缓存路径,
* 否则就调用getCacheDir()方法来获取缓存路径。
* 前者获取到的就是 /sdcard/Android/data/<application package>/cache
* 而后者获取到的是 /data/data/<application package>/cache 。
*
* */
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
} File dir = new File(cachePath + File.separator + uniqueName);
Log.d(TAG, "缓存目录:" + dir.getAbsolutePath()); return dir;
} //版本名
public static String getVersionName(Context context) {
return getPackageInfo(context).versionName;
} //版本号
public static int getVersionCode(Context context) {
return getPackageInfo(context).versionCode;
} private static PackageInfo getPackageInfo(Context context) {
PackageInfo pi = null; try {
PackageManager pm = context.getPackageManager();
pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS); return pi;
} catch (Exception e) {
e.printStackTrace();
} return pi;
} public static String urlToKey(String url) {
return getMD5(url);
} /*
* 传入一个字符串String msg,返回Java MD5加密后的16进制的字符串结果。
* 结果形如:c0e84e870874dd37ed0d164c7986f03a
*/
public static String getMD5(String msg) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
md.reset();
md.update(msg.getBytes());
byte[] bytes = md.digest(); String result = "";
for (byte b : bytes) {
// byte转换成16进制
result += String.format("%02x", b);
} return result;
}
}

涉及到网络和读写存储,不要忘记加权限:

 <!-- SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 向SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"></uses-permission>

附录文章:

1,《Android第三方异步网路加载库AsyncHttpClient内部实现缓存策略了吗?》链接:http://blog.csdn.net/zhangphil/article/details/48595817 


2,《Android图片加载与缓存开源框架:Android Glide》链接:http://blog.csdn.net/zhangphil/article/details/45535693


3,《Android获取App版本号和版本名》链接:http://blog.csdn.net/zhangphil/article/details/43795099


4,《基于Java LinkedList,实现Android大数据缓存策略》链接:http://blog.csdn.net/zhangphil/article/details/44116885


5,《使用新式LruCache取代SoftReference缓存图片,Android异步加载图片》链接:http://blog.csdn.net/zhangphil/article/details/43667415


6,《使用Android新式LruCache缓存图片,基于线程池异步加载图片》链接:http://blog.csdn.net/zhangphil/article/details/44082287


7,《Java MD5(字符串)》链接:http://blog.csdn.net/zhangphil/article/details/44152077


8,《Android OkHttp(1)》链接:http://blog.csdn.net/zhangphil/article/details/51861503


9,《Android二级缓存之物理存储介质上的缓存DiskLruCache》链接:http://blog.csdn.net/zhangphil/article/details/51888974

Android OkHttp与物理存储介质缓存:DiskLruCache(2)的更多相关文章

  1. Android okHttp网络请求之缓存控制Cache-Control

    前言: 前面的学习基本上已经可以完成开发需求了,但是在项目中有时会遇到对请求做个缓存,当没网络的时候优先加载本地缓存,基于这个需求我们来学习一直okHttp的Cache-Control. okHttp ...

  2. Android okHttp网络请求之Json解析

    前言: 前面两篇文章介绍了基于okHttp的post.get请求,以及文件的上传下载,今天主要介绍一下如何和Json解析一起使用?如何才能提高开发效率? okHttp相关文章地址: Android o ...

  3. Android okHttp网络请求之Get/Post请求

    前言: 之前项目中一直使用的Xutils开源框架,从xutils 2.1.5版本使用到最近的xutils 3.0,使用起来也是蛮方便的,只不过最近想着完善一下app中使用的开源框架,由于Xutils里 ...

  4. Android okHttp网络请求之文件上传下载

    前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...

  5. Android okHttp网络请求之Retrofit+Okhttp+RxJava组合

    前言: 通过上面的学习,我们不难发现单纯使用okHttp来作为网络库还是多多少少有那么一点点不太方便,而且还需自己来管理接口,对于接口的使用的是哪种请求方式也不能一目了然,出于这个目的接下来学习一下R ...

  6. Android二级缓存之物理存储介质上的缓存DiskLruCache

     Android二级缓存之物理存储介质上的缓存DiskLruCache Android DiskLruCache属于物理性质的缓存,相较于LruCache缓存,则DiskLruCache属于And ...

  7. Cache【硬盘缓存工具类(包含内存缓存LruCache和磁盘缓存DiskLruCache)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 内存缓存LruCache和磁盘缓存DiskLruCache的封装类,主要用于图片缓存. 效果图 代码分析 内存缓存LruCache和 ...

  8. Android内存优化之磁盘缓存

    前言: 在上一篇文章中介绍了内存缓存,内存缓存的优点就是很快,但是它又有缺点: 空间小,内存缓存不可能很大: 内存紧张时可能被清除: 在应用退出时就会消失,做不到离线: 基于以上的缺点有时候又需要另外 ...

  9. Android OkHttp(1)

     Android OkHttp(1) OkHttp是一个流行的第三方开源网络请求框架,在目前的一些APP开发中比较流行.Android平台开源的网络请求框架不少,比如常见的Volley, Asyn ...

随机推荐

  1. PHP 官方说明

    http://php.net/manual/en/mysqli.affected-rows.php The above examples will output: Affected rows (INS ...

  2. hdu 1430 魔板 康托展开 + 很好的映射

    http://acm.hdu.edu.cn/showproblem.php?pid=1430 如果从start ---> end,每一次都bfs进行,那么就肯定会超时. 考虑到先把start映射 ...

  3. [转]C#Linq中的Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods,skip,take,takewhile,skipwhile,编译查询等

    本文转自:http://www.cnblogs.com/suizhikuo/p/3791799.html 我们继续讲解LINQ to SQL语句,这篇我们来讨论Union All/Union/Inte ...

  4. vue.js学习参考手册

    参考手册 示例:www.51siyuan.cn/161.html

  5. file.seek()

    语法:fileObject.seek(offset,whence) offset -- 开始的偏移量,也就是代表需要移动偏移的字节数 whence:可选,默认值为 0.给offset参数一个定义,表示 ...

  6. 迅为iTOP-4412物联网开发板入门学习高手进阶项目开发超树莓派

    免费视频教程: 为初学者精心录制的整套视频教程全部免费,随IT技术发展而不断增添的视频教程仍然免费!一支有经验的工程师团队会始终成为您的后盾. 项目实战---全开源: 手机远程控制开发板 门禁系统 W ...

  7. Android(java)学习笔记200:JNI之NDK的概念

    1.交叉编译 (1)概念 在一个平台(硬件)和os(软件)环境下,编译出另一种平台和os下可以运行的二进制代码. e.g:     电脑端                               ...

  8. CREATE INDEX - 定义一个新索引

    SYNOPSIS CREATE [ UNIQUE ] INDEX name ON table [ USING method ] ( { column | ( expression ) } [ opcl ...

  9. java_String类练习

    public class StringTest { //1.模拟trim方法,去除字符串两端的空格 public static void main(String[] args) { String st ...

  10. ubuntu 网卡配置