三级缓存的提出就是为了提升用户体验。当我们第一次打开应用获取图片时,先到网络去下载图片,然后依次存入内存缓存,磁盘缓存,当我们再一次需要用到刚才下载的这张图片时,就不需要再重复的到网络上去下载,直接可以从内存缓存和磁盘缓存中找,由于内存缓存速度较快,我们优先到内存缓存中寻找该图片,如果找到则运用,如果没有找到(内存缓存大小有限),那么我们再到磁盘缓存中去找。只要我们合理的去协调这三层缓存运用,便可以提升应用性能。三级缓存指的是:内存缓存、本地缓存、网络缓存。其各自的特点是内存缓存速度快, 优先读取,本地缓存速度其次, 内存没有,读本地,网络缓存速度最慢, 本地也没有,才访问网络。对于网络缓存理解起来较为容易直接从网络中获取资源,本地缓存可以存在SD卡中,内存缓存一般存在数组或集合中。需要在注意的是,数组和集合的生命周期依赖于它存在的activity中,因此当程序退出,一般情况下数组和集合中的资源会被释放。在具体了解三级缓存的工作原理之前有必要先介绍几个概念。

实例和对象:
          对象是类的一个实例,创建对象的过程也叫类的实例化。对象是以类为模板来创建的。这样在安卓的底部就会用堆来存储对象的实例,栈来存储类的对象。引用是指某些对象的实例化需要其它的对象实例,比如ImageView的实例化就需要Context对象,就是表示ImageView对于Context持有引用(ImageView holds a reference to Context)。

垃圾回收机制(GC):
          对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。更细致来讲就是对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常GC采用有向图的方式记录并管理堆中的所有对象,通过这种方式确定哪些对象时“可达”,哪些对象时“不可达”。当对象不可达的时候,即对象不再被引用的时候,就会被垃圾回收。该机制对虚拟机中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop)的保证虚拟机中的内存空间,防止出现内存泄露和溢出问题。

内存泄露:
          当不再需要某个实例后,但是这个对象却仍然被引用,这个情况就叫做内存泄露(Memory Leak)。安卓虚拟机为每一个应用分配一定的内存空间,当内存泄露到达一定的程度就会造成内存溢出。

内存的引用:
          内存的引用级别包括,强引用、软引用、弱引用、虚引用。强引用是默认的引用方式, 即使内存溢出,也不会回收。软引用(softReference), 内存不够时, 会考虑回收。 弱引用 (WeakReference)内存不够时, 更会考虑回收。虚引用(PhantomReference) 内存不够时, 最优先考虑回收! 一般我们常用到的引用就是强引用,比如引用创建一个成员变量里面的引用。对于GC来说, SoftReference的强度明显低于 SrongReference。SoftReference修饰的引用,其告诉GC:我是一个 软引用,当内存不足的时候,我指向的这个内存是可以给你释放掉的。一般对于这种占用内存资源比较大的,又不是必要的变量;或者一些占用大量内存资源的一些缓存的变量,就需要考虑 SoftReference。对于GC来说, WeakReference 的强度又明显低于 SoftReference 。 WeakReference 修饰的引用,其告诉GC:我是一个弱引用,对于你的要求我没有话说,我指向的这个内存是可以给你释放掉的。虚引用其实和上面讲到的各种引用不是一回事的,他主要是为跟踪一个对象何时被GC回收。在android里面也是有用到的:FileCleaner.java 。这些避免内存溢出的引用方式在Android 2.3+的版本上已经不再起太大作用, 因为垃圾回收器会频繁回收非强引用的对象, Android官方建议使用LRUCache。所以当我们用软引用进行内存缓存时会发现内存中的资源会被系统频繁回收。最终是从本地进行读数据。

这样我们就能很好的理解要三级缓存了。首先,在内存读数据。内存中读数据需要用到最近最少引用算法(lrucache)。Lrucache算法要求为new LruCache<String, Bitmap>()传入一个分配给软件的最大内存,同时重写sizeof()方法,计算每一张图片的大小。这样就可以直接调用LruCache的put()和get()方法。当发现内存中没用数据是时,找到SD卡中的存储文件。通过Bitmap的compress()方法向文件夹中写数据,通过位图工厂BitmapFactory的decodeStream()读取数据,同时可以为decodeStream()方法传入options参数,缩小图片。最后如果,本地仍然没有获取数据,在从网络获取。网络获取数据可以用异步任务来执行(耗时操作不能再主线程中执行)。异步任务需要重写onPostExecute()方法和doInBackground()方法。doInBackground()方法中访问网路,这里用到的是Httpurlconnection,通过连接得到输入流,利用位图工厂转换成位图,返回。onPostExecute()方法在doInBackground()方法执行后执行,传入的参数数doInBackground()方法的返回值。

接下来是代码实,这是我调用工具类的写法:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView; import com.example.huang.demo.utils.CacheUtils; public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity"; private ImageView iv; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getBitmap();
} private void getBitmap() {
iv = (ImageView) findViewById(R.id.iv);
CacheUtils utils = new CacheUtils();
utils.diaplay(iv,"http://192.168.23.48:8080/test.jpg");
}
}

布局文件只要一个imageview就不单独写出来,直接来看缓存工具类:

import android.graphics.Bitmap;
import android.util.Log;
import android.widget.ImageView; /**
* Created by huang on 2016/12/3.
*/ public class CacheUtils {
private static final String TAG = "CacheUtils";
private MemoryCacheUtils mMemoryCacheUtils;
private LocalCacheUtils mLocalCacheUtils;
private NetCacheUtils mNetCacheUtils; public CacheUtils() {
mMemoryCacheUtils = new MemoryCacheUtils();
mLocalCacheUtils = new LocalCacheUtils();
mNetCacheUtils = new NetCacheUtils(mMemoryCacheUtils, mLocalCacheUtils);
} public void diaplay(ImageView imageView, String url) { //内存缓存 生命周期同调用者
Bitmap bitmap = mMemoryCacheUtils.getBitmapToMemory(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
Log.i(TAG, "diaplay: 221111111111");
return;
} //本地缓存
bitmap = LocalCacheUtils.getBitmapToLoacl(url);
if (bitmap != null) {
Log.i(TAG, "diaplay: 1111111");
imageView.setImageBitmap(bitmap);
mMemoryCacheUtils.putBitmapToMemory(bitmap, url);
return;
} //网络缓存
mNetCacheUtils.getBitmapFromNet(imageView, url);
}
}

内存缓存的工具类

import android.graphics.Bitmap;
import android.util.Log;
import android.util.LruCache; import static android.content.ContentValues.TAG; /**
* Created by huang on 2016/12/3.
*/ public class MemoryCacheUtils { private LruCache<String, Bitmap> mMemoryCache; public MemoryCacheUtils() {
int maxmemory = (int) Runtime.getRuntime().maxMemory();
Log.i(TAG, "MemoryCacheUtils: " + maxmemory);
mMemoryCache = new LruCache<String, Bitmap>(maxmemory / 8) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
} public void putBitmapToMemory(Bitmap bitmap, String url) {
Log.i(TAG, "putBitmapToMemory: ");
mMemoryCache.put(url, bitmap);
} public Bitmap getBitmapToMemory(String url) {
Log.i(TAG, "getBitmapToMemory: ");
Bitmap bitmap = mMemoryCache.get(url);
return bitmap;
}
}

本地缓存(SD卡)的工具类:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import static android.content.ContentValues.TAG; /**
* Created by huang on 2016/12/3.
*/ public class LocalCacheUtils {
public static void putBitmapToLoacl(Bitmap bitmap, String url) {
String encode = Md5Utils.encode(url);
File file = new File(Environment.getExternalStorageDirectory(), encode);
Log.i(TAG, "putBitmapToLoacl: " + file.toString());
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
try {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} public static Bitmap getBitmapToLoacl(String url) {
String encode = Md5Utils.encode(url);
File file = new File(Environment.getExternalStorageDirectory(), encode);
if (file.exists()) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 3;
try {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, opts);
return bitmap;
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
}
return null;
}
}

网络缓存的工具类:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView; import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; /**
* Created by huang on 2016/12/3.
*/ public class NetCacheUtils {
private MemoryCacheUtils mMemoryCacheUtils;
private LocalCacheUtils mLocalCacheUtils; public NetCacheUtils(MemoryCacheUtils mMemoryCacheUtils, LocalCacheUtils mLocalCacheUtils) {
this.mLocalCacheUtils = mLocalCacheUtils;
this.mMemoryCacheUtils = mMemoryCacheUtils;
} public void getBitmapFromNet(ImageView imageView, String url) {
imageView.setTag(url);
Bitmaptask task = new Bitmaptask();
task.execute(imageView, url);
} class Bitmaptask extends AsyncTask<Object, Void, Bitmap> { private HttpURLConnection urlConnection;
private ImageView imageView;
private String url; @Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
if (url.equals(imageView.getTag())) {
imageView.setImageBitmap(bitmap);
System.out.print("onPostExecute");
mLocalCacheUtils.putBitmapToLoacl(bitmap, url);
mMemoryCacheUtils.putBitmapToMemory(bitmap, url);
}
}
} @Override
protected Bitmap doInBackground(Object[] params) {
imageView = (ImageView) params[0];
url = (String) params[1];
Bitmap bitmap = downloadFromNet(url);
return bitmap;
} private Bitmap downloadFromNet(String url) {
try {
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setConnectTimeout(5000);
urlConnection.setRequestMethod("GET");
int responseCode = urlConnection.getResponseCode();
if (responseCode == 200) {
InputStream inputStream = urlConnection.getInputStream();
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, opts);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
urlConnection.disconnect();
}
return null;
}
}
}

本地缓存中用到的MD5Utils工具类:

import java.security.MessageDigest;

/**
* Created by huang on 2016/12/3.
*/ public class Md5Utils {
public static String encode(String pwd) {
try {
MessageDigest digest = MessageDigest.getInstance("md5");
byte[] bs = digest.digest(pwd.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : bs) {
int number = b & 0xff;
String str = Integer.toHexString(number);
if (str.length() == 1) {
sb.append("0");
}
sb.append(number);
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

最会别忘了添加权限:

    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

关于Android中的三级缓存的更多相关文章

  1. android 项目学习随笔十八(三级缓存)

    xUtils的BitmapUtils模块用的就是三级缓存,在项目中尽量还是应用BitmapUtils 三级缓存(机制) import com.itheima.zhsh66.R; import andr ...

  2. 简单地Android中图片的三级缓存机制

    我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存 ...

  3. Android中图片的三级缓存策略

    在开发过程中,经常会碰到进行请求大量的网络图片的样例.假设处理的不好.非常easy造成oom.对于避免oom的方法,无非就是进行图片的压缩.及时的回收不用的图片.这些看似简单可是处理起来事实上涉及的知 ...

  4. Android中图片的三级缓存

    为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi ...

  5. Android 的图片异步请求加三级缓存 ACE

    使用xUtils等框架是很方便,但今天要用代码实现bitmapUtils 的功能,很简单, 1 AsyncTask请求一张图片 ####AsyncTask #####AsyncTask是线程池+han ...

  6. Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference))

    因为之前项目同事使用了图片三级缓存,今天整理项目的时候发现同事还是使用了软引用(SoftRefrerence)和弱引用(WeakReference),来管理在内存中的缓存.看到这个我就感觉不对了.脑海 ...

  7. 【Android - 进阶】之图片三级缓存的原理及实现

    在Android开发中,如果图片过多,而我们又没有对图片进行有效的缓存,就很容易导致OOM(Out Of Memory)错误.因此,图片的缓存是非常重要的,尤其是对图片非常多的应用.现在很多框架都做了 ...

  8. Android进阶图片处理之三级缓存方案

    图片的三级缓存 一.概述 一開始在学习Android的时候.处理图片的时候,每次获取图片都是直接从网络上面载入图片. 可是在开发项目的过程中,每次点击进入app里面,图片都要慢慢的再一次从网络上面载入 ...

  9. 缓存AsimpleCache -- 解决Android中Sharedpreferences无法存储List数据/ASimpleCache

    Sharedpreferences想必大家在项目中都经常会用到,但是如果需要在本地需要存储比较多的数据,存储一个集合的时,发现Sharedpreferences并不 是那么好使了. 分析 如果需要在本 ...

随机推荐

  1. 绿色版的Linux.NET——“Jws.Mono”

    Linux.NET环境的搭建,不仅是一项比较耗时的事情,同时也是一项略显复杂繁琐的事情.特别是对于近期的几个Mono版本,由于官方所提供的源码包中出现代码文件的缺失,这总让我们的付出变得徒劳.另外一方 ...

  2. ENode框架单台机器在处理Command时的设计思路

    设计目标 尽量快的处理命令和事件,保证吞吐量: 处理完一个命令后不需要等待命令产生的事件持久化完成就能处理下一个命令,从而保证领域内的业务逻辑处理不依赖于持久化IO,实现真正的in-memory: 保 ...

  3. java的继承练习

     看程序写结果:    A:一个类的静态代码块,构造代码块,构造方法的执行流程    静态代码块 > 构造代码块 > 构造方法   B:静态的内容是随着类的加载而加载    静态代码块的内 ...

  4. iOS 开发不可缺少的工具

    1.截屏利器:Snip 强大的滚动截屏功能,你值得拥有! Snip.png 传送门:http://www.snip.qq.com/ 2.Mac上最好的终端模拟器:iTerm2 iTeam.png 传送 ...

  5. jquery库和cityselect插 件的省市 级联

    /*$(function(){ $("#select_provice").citySelect({ prov:"北京", nodata:"none&q ...

  6. 从零开始编写自己的C#框架(22)——添加普通列表页面

    普通列表页面指的是上一章那种有层次感列表以外的正常列表页面,由于上一章已讲解了正常添加页面的相关操作了,所以部分相关的操作本章节就不再罗嗦重复一次了.大家可以试试先用本章内容中的一些简单介绍,自己使用 ...

  7. spring mvc DispatcherServlet详解之前传---FrameworkServlet

    做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationCont ...

  8. SpringMVC一路总结(一)

    SpringMVC听闻已久,早在去年就被学长问到关于SpringMVC的基础知识,当时也没在意.主要是工作中也没有用到关于SpringMVC的技术,因此免于没有时间和精力的借口就没有接触和学习Spri ...

  9. MVP社区巡讲-云端基础架构:12月5日北京站 12月12日上海站

    紧跟当今的技术发展趋势还远远不够,我们要引领变革!加入本地技术专家社区,获取真实案例.实况培训演示以及探讨新一代解决方案.在此活动中,您将: 了解如何运用开源(OSS)技术.Microsoft 技术及 ...

  10. ASP.NET Core 中文文档 第二章 指南(4.8)添加新的字段

    原文 Adding a New Field 作者 Rick Anderson 翻译 谢炀(Kiler) 校对 许登洋(Seay).高嵩(Jack) 在这个章节你将使用 Entity Framework ...