关于Android中的三级缓存

三级缓存的提出就是为了提升用户体验。当我们第一次打开应用获取图片时,先到网络去下载图片,然后依次存入内存缓存,磁盘缓存,当我们再一次需要用到刚才下载的这张图片时,就不需要再重复的到网络上去下载,直接可以从内存缓存和磁盘缓存中找,由于内存缓存速度较快,我们优先到内存缓存中寻找该图片,如果找到则运用,如果没有找到(内存缓存大小有限),那么我们再到磁盘缓存中去找。只要我们合理的去协调这三层缓存运用,便可以提升应用性能。三级缓存指的是:内存缓存、本地缓存、网络缓存。其各自的特点是内存缓存速度快, 优先读取,本地缓存速度其次, 内存没有,读本地,网络缓存速度最慢, 本地也没有,才访问网络。对于网络缓存理解起来较为容易直接从网络中获取资源,本地缓存可以存在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" />
时间: 2024-07-29 06:21:17

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

Android中使用SoftReference缓存图片对象

Java中的SoftReference即对象的软引用.如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它:如果内存空间不足了, 就会回收这些对象的内存.只要垃圾回收器没有回收它,该对象就可以被程序使用.软引用可用来实现内存敏感的高速缓存.使用软引用能防止内存泄露,增强程序的健壮性.   SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该 Java对象的回收.也就是说,一旦SoftReference保存了对一个Java对象的

Android --- 简单实现三级缓存LruCache

三级缓存: 1.网络缓存 从网络获取资源 2.本地缓存 从本地获取数据 3.内存缓存 从内存获取数据 内存缓存:主要是用到了LruCache这个类,这个类比较适合用来缓存图片,它会将强引用对象放在LineedHashMap中,当缓存数据大小达到预定值的时候会将在该集合中比较少使用的对象从内存中移除. package com.itljw.zhbj.util; import android.graphics.Bitmap; import android.util.LruCache; /** * Cr

Android三级缓存机制工具类的实现

一.三级缓存概述 (一)三级缓存的三级 第一级是内存,最快,不需要网络 第二级是本地,不需要网络 第三级是网络,需要网络请求 三级缓存机制的思想: 如果在内存中获取到数据,就不去本地和网络中获取. 如果在本地中获取到数据就不去网络中获取, 如果内存和本地中不存在数据,就要去网络中请求数据 三级缓存技术能有效节省用户的流量,但是也会增加一些内存负担. 二.使用示例展示三级缓存工具栏类的使用 程序运行后的页面: 虽然只用一个按钮和一个图片显示,但是通过测试(联网状态和断网状态对比)能知道图片是从网络

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

我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是LruCache中.如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中:读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则

Android开发中图片的三级缓存策略

一.简介 现在的Android应用程序中,不可避免的都会使用到图片,如果每次加载图片的时候都要从网络重新拉取,这样不但很耗费用户的流量,而且图片加载的也会很慢,用户体验很不好.所以一个应用的图片缓存策略是很重要的.通常情况下,Android应用程序中图片的缓存策略采用"内存-本地-网络"三级缓存策略,首先应用程序访问网络拉取图片,分别将加载的图片保存在本地SD卡中和内存中,当程序再一次需要加载图片的时候,先判断内存中是否有缓存,有则直接从内存中拉取,否则查看本地SD卡中是否有缓存,SD

Android中图片的三级缓存策略

一.简介 现在的Android应用程序中,不可避免的都会使用到图片,如果每次加载图片的时候都要从网络重新拉取,这样不但很耗费用户的流量,而且图片加载的也会很慢,用户体验很不好.所以一个应用的图片缓存策略是很重要的.通常情况下,Android应用程序中图片的缓存策略采用"内存-本地-网络"三级缓存策略,首先应用程序访问网络拉取图片,分别将加载的图片保存在本地SD卡中和内存中,当程序再一次需要加载图片的时候,先判断内存中是否有缓存,有则直接从内存中拉取,否则查看本地SD卡中是否有缓存,SD

Android实战——RxJava2解锁图片三级缓存框架

RxJava2解锁图片三级缓存框架 本篇文章包括以下内容 前言 图片三级缓存的介绍 框架结构目录的介绍 构建项目整体框架 实现图片三级缓存 演示效果 源码下载 结语 前言 RxJava2作为如今那么主流的技术,不学习学习都不行了,本篇文章需要你有RxJava2的基础,如果需要对RxJava2学习的同学,可以关注我的博客,查看Android实战--RxJava2+Retrofit+RxBinding解锁各种新姿势 .项目代码实现模仿Picasso,大伙们可以看下最后的代码效果,那么废话不多说,He

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

图片的三级缓存 一.概述 一開始在学习Android的时候.处理图片的时候,每次获取图片都是直接从网络上面载入图片. 可是在开发项目的过程中,每次点击进入app里面,图片都要慢慢的再一次从网络上面载入. 给用户的体验很不好,第一个等待的时间很令人dan 疼 第二个给用户的流量造成了不必要的浪费 因此提出图片的三级缓存策略, 所谓的三级缓存:就是在手机载入图片的时候, 1.首先从内存中载入, 2.假设内存中没有的话,从sd卡上获取.读取到之后将图片写入到内存中 3.假设sd卡上没有的话,从网络上获

Android图片三级缓存策略

1.简介 Android缓存原理都是一样,可以自己封装. 三级缓存: 1.内存缓存:缓存在内存中,基于LRU(least recently used )算法,机器重启消失. 2.本地缓存.缓存在本地中.一般键值对形式.(url,filepath) 3.网络缓存.从网络加载资源,然后缓存在内存.本地中. 2.实现步骤 2.1 内存缓存: [java] view plain copypublic class MemoryCacheUtils { private LruCache<String,Bit