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. 贪心/思维题 Codeforces Round #310 (Div. 2) C. Case of Matryoshkas

    题目传送门 /* 题意:套娃娃,可以套一个单独的娃娃,或者把最后面的娃娃取出,最后使得0-1-2-...-(n-1),问最少要几步 贪心/思维题:娃娃的状态:取出+套上(2),套上(1), 已套上(0 ...

  2. Nuget 自定义配置(官网)

    <?xml version="1.0" encoding="utf-8"?> <configuration> <config> ...

  3. D. Dasha and Very Difficult Problem 二分

    http://codeforces.com/contest/761/problem/D c[i] = b[i] - a[i],而且b[]和a[]都属于[L, R] 现在给出a[i]原数组和c[i]的相 ...

  4. python 使用 Pyscript 调试 报错

    UnicodeEncodeError: 'ascii' codec can't encode characters in position 13-16: ordinal not in range(12 ...

  5. android环境搭建环境 cordova run android gradle wrapper报错

    cordova run android命令报错 Error: Could not find an installed version of Gradle either in Android Studi ...

  6. SQL——时间戳

    mysql 低版本,date.datetime.timestamp 无法精确到毫秒 可以舍弃时间类型字段,用 bigint 来代替,如果用字符串类型代替,还是比较担心排序的时候只是根据第一个字母进行排 ...

  7. git 出现 The current branch is not configured for pull No value for key branch.master.merge found in configuration

    以下是我在网上找到的不错的文章,我参考后已解决我的问题: http://my.oschina.net/robinsonlu/blog/144085 http://www.cnblogs.com/zha ...

  8. iTOP4418开发板7寸屏幕Android系统下横竖屏设置

    Android系统屏幕旋转设置 平台: iTOP4418开发板+7寸屏幕 1. Androd4.4源码可以编译成手机模式和平板模式,讯为iTop4418 开发平台的Android系统默认编译为平板模式 ...

  9. 如何优雅地从CSDN转载文章

    复制粘贴应该是最显而易见的方法,但是不仅会有丢失内容,而且格式也会丢失.要想达到更好的效果,可以从html源码入手. 1.在chrome浏览器中打开要转载的文章,右键选择检查 2.在chrome的右方 ...

  10. incremental linking(增量链接)的作用

    转:incremental linking(增量链接)的作用 今天编译一个C++程序时,报了一个奇怪的错误(之前是好好的): 1>LINK : fatal error LNK1123: fail ...