Android OkHttp与物理存储介质缓存:DiskLruCache(2)
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)的更多相关文章
- Android okHttp网络请求之缓存控制Cache-Control
前言: 前面的学习基本上已经可以完成开发需求了,但是在项目中有时会遇到对请求做个缓存,当没网络的时候优先加载本地缓存,基于这个需求我们来学习一直okHttp的Cache-Control. okHttp ...
- Android okHttp网络请求之Json解析
前言: 前面两篇文章介绍了基于okHttp的post.get请求,以及文件的上传下载,今天主要介绍一下如何和Json解析一起使用?如何才能提高开发效率? okHttp相关文章地址: Android o ...
- Android okHttp网络请求之Get/Post请求
前言: 之前项目中一直使用的Xutils开源框架,从xutils 2.1.5版本使用到最近的xutils 3.0,使用起来也是蛮方便的,只不过最近想着完善一下app中使用的开源框架,由于Xutils里 ...
- Android okHttp网络请求之文件上传下载
前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...
- Android okHttp网络请求之Retrofit+Okhttp+RxJava组合
前言: 通过上面的学习,我们不难发现单纯使用okHttp来作为网络库还是多多少少有那么一点点不太方便,而且还需自己来管理接口,对于接口的使用的是哪种请求方式也不能一目了然,出于这个目的接下来学习一下R ...
- Android二级缓存之物理存储介质上的缓存DiskLruCache
Android二级缓存之物理存储介质上的缓存DiskLruCache Android DiskLruCache属于物理性质的缓存,相较于LruCache缓存,则DiskLruCache属于And ...
- Cache【硬盘缓存工具类(包含内存缓存LruCache和磁盘缓存DiskLruCache)】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 内存缓存LruCache和磁盘缓存DiskLruCache的封装类,主要用于图片缓存. 效果图 代码分析 内存缓存LruCache和 ...
- Android内存优化之磁盘缓存
前言: 在上一篇文章中介绍了内存缓存,内存缓存的优点就是很快,但是它又有缺点: 空间小,内存缓存不可能很大: 内存紧张时可能被清除: 在应用退出时就会消失,做不到离线: 基于以上的缺点有时候又需要另外 ...
- Android OkHttp(1)
Android OkHttp(1) OkHttp是一个流行的第三方开源网络请求框架,在目前的一些APP开发中比较流行.Android平台开源的网络请求框架不少,比如常见的Volley, Asyn ...
随机推荐
- 贪心/思维题 Codeforces Round #310 (Div. 2) C. Case of Matryoshkas
题目传送门 /* 题意:套娃娃,可以套一个单独的娃娃,或者把最后面的娃娃取出,最后使得0-1-2-...-(n-1),问最少要几步 贪心/思维题:娃娃的状态:取出+套上(2),套上(1), 已套上(0 ...
- Nuget 自定义配置(官网)
<?xml version="1.0" encoding="utf-8"?> <configuration> <config> ...
- 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]的相 ...
- python 使用 Pyscript 调试 报错
UnicodeEncodeError: 'ascii' codec can't encode characters in position 13-16: ordinal not in range(12 ...
- android环境搭建环境 cordova run android gradle wrapper报错
cordova run android命令报错 Error: Could not find an installed version of Gradle either in Android Studi ...
- SQL——时间戳
mysql 低版本,date.datetime.timestamp 无法精确到毫秒 可以舍弃时间类型字段,用 bigint 来代替,如果用字符串类型代替,还是比较担心排序的时候只是根据第一个字母进行排 ...
- 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 ...
- iTOP4418开发板7寸屏幕Android系统下横竖屏设置
Android系统屏幕旋转设置 平台: iTOP4418开发板+7寸屏幕 1. Androd4.4源码可以编译成手机模式和平板模式,讯为iTop4418 开发平台的Android系统默认编译为平板模式 ...
- 如何优雅地从CSDN转载文章
复制粘贴应该是最显而易见的方法,但是不仅会有丢失内容,而且格式也会丢失.要想达到更好的效果,可以从html源码入手. 1.在chrome浏览器中打开要转载的文章,右键选择检查 2.在chrome的右方 ...
- incremental linking(增量链接)的作用
转:incremental linking(增量链接)的作用 今天编译一个C++程序时,报了一个奇怪的错误(之前是好好的): 1>LINK : fatal error LNK1123: fail ...