三级缓存的提出就是为了提升用户体验。当我们第一次打开应用获取图片时,先到网络去下载图片,然后依次存入内存缓存,磁盘缓存,当我们再一次需要用到刚才下载的这张图片时,就不需要再重复的到网络上去下载,直接可以从内存缓存和磁盘缓存中找,由于内存缓存速度较快,我们优先到内存缓存中寻找该图片,如果找到则运用,如果没有找到(内存缓存大小有限),那么我们再到磁盘缓存中去找。只要我们合理的去协调这三层缓存运用,便可以提升应用性能。三级缓存指的是:内存缓存、本地缓存、网络缓存。其各自的特点是内存缓存速度快, 优先读取,本地缓存速度其次, 内存没有,读本地,网络缓存速度最慢, 本地也没有,才访问网络。对于网络缓存理解起来较为容易直接从网络中获取资源,本地缓存可以存在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。所以当我们用软引用进行内存缓存时会发现内存中的资源会被系统频繁回收。最终是从本地进行读数据。
public class BitmapUtils {
Context context;
//图片本地缓存路径
private final static String SDCARD_CACHE = Environment.getExternalStorageDirectory() + "/imagecache";
//图片存放文件夹
File fileDir = new File(SDCARD_CACHE);
private Map<String, SoftReference<Bitmap>> map = new HashMap<String, SoftReference<Bitmap>>();
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
ImageViewToBitmap imageViewToBitmap = (ImageViewToBitmap) msg.obj;
imageViewToBitmap.iv.setImageBitmap(imageViewToBitmap.bitmap);
break;
}
}
};
//构造方法
public BitmapUtils(Context context) {
this.context = context;
if (!fileDir.exists()) {
fileDir.mkdirs();
}
}
//加载图片的方法
public void display(ImageView iv, String url) {
//内存缓存
Bitmap bitmap = loadMemory(url);
if (bitmap != null) {
iv.setImageBitmap(bitmap);
} else {
//sdcard缓存
bitmap = loadSD(url);
if (bitmap != null) {
iv.setImageBitmap(bitmap);
} else {
//网络缓存
loadInternetImage(iv, url);
}
}
}
//内存缓存
private Bitmap loadMemory(String url) {
SoftReference<Bitmap> value = map.get(url);
if (value != null) {
Bitmap bitmap = value.get();
return bitmap;
}
return null;
}
//获取本地图片
private Bitmap loadSD(String url) {
String name = getFileName(url);
File file = new File(fileDir, name);
if (file.exists()) {
//BitmapFactory选项
BitmapFactory.Options options = new BitmapFactory.Options();
//加载图片宽高
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
//获取图片和手机屏幕宽高
int outWidth = options.outWidth;
int outHeight = options.outHeight;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int widthPixels = displayMetrics.widthPixels;
int heightPixels = displayMetrics.heightPixels;
//图片跟手机屏幕进行比对
int scale = 0;
int scaleX = outWidth / widthPixels;
int scaleY = outHeight / heightPixels;
scale = scaleX > scaleY ? scaleX : scaleY;
if (scale == 0) {
scale = 1;
}
options.inJustDecodeBounds = false;
options.inSampleSize = scale;
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
Log.i("aaa",file.getAbsolutePath().toString());
//内存缓存
SoftReference<Bitmap> value = new SoftReference<Bitmap>(bitmap);
map.put(url, value);
Log.i("sss",name);
return bitmap;
}
return null;
}
//获取网络图片
private void loadInternetImage(ImageView iv, String url) {
//开子线程做耗时操作
new Thread(new DownloadImage(iv, url)).start();
}
private class DownloadImage implements Runnable {
ImageView iv;
String url;
private InputStream inputStream;
private FileOutputStream fos;
public DownloadImage(ImageView iv, String url) {
this.iv = iv;
this.url = url;
}
@Override
public void run() {
try {
URL path = new URL(url);
HttpURLConnection conn = (HttpURLConnection) path.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
inputStream = conn.getInputStream();
String name = getFileName(url);
File file = new File(fileDir, name);
fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
Log.i("xxx",name);
Bitmap bitmap = loadSD(url);
//ImageView转换成Bitmap 临时对象
ImageViewToBitmap ivtb = new ImageViewToBitmap(iv, bitmap);
Message message = Message.obtain(handler, 0, ivtb);
message.sendToTarget();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//获取图片的名称
private String getFileName(String url) {
// /a.jpg
Log.i("xxx", url.substring(url.lastIndexOf("/") + 1));
return url.substring(url.lastIndexOf("/") + 1);
// return Md5Utils.encode(url) + ".jpg";
}
//ImageView转换成Bitmap转换成
private class ImageViewToBitmap {
ImageView iv;
Bitmap bitmap;
public ImageViewToBitmap(ImageView iv, Bitmap bitmap) {
this.iv = iv;
this.bitmap = bitmap;
}
}
}
public class Md5Utils {
public static String encode(String password){
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] result = digest.digest(password.getBytes());
StringBuffer sb = new StringBuffer();
for(byte b : result){
int number = (int)(b & 0xff) ;
String str = Integer.toHexString(number);
if(str.length()==1){
sb.append("0");
}
sb.append(str);
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
//can't reach
return "";
}
}
}
这里是三级缓存的用法
public class MainActivity extends AppCompatActivity {
private String url = “http://d.hiphotos.baidu.com/zhidao/pic/item/72f082025aafa40fe871b36bad64034f79f019d4.jpg“;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//找控件
ImageView iv = (ImageView) findViewById(R.id.iv);
//实例化图片加载框架
BitmapUtils bitmapUtils = new BitmapUtils(this);
bitmapUtils.display(iv, url);
}
}
“`