三级缓存的基本使用

三级缓存:

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

    1、网络缓存 从网络获取资源(异步加载) 
    2、本地缓存 从本地获取数据(File存储) 

    3、内存缓存 从内存获取数据(LruCache)


1、内存缓存:LruCache

  主要是用到了LruCache这个类,这个类比较适合用来缓存图片,它会将强引用对象放在LinkedHashMap中,当缓存数据大小达到预定值的时候会将在该集合中比较少使用的对象从内存中移除。

import android.graphics.Bitmap;
import android.util.LruCache;

/**
 * Created by lenovo on 2018/5/18.
 */

public class MemoryCacheUtil {
    private LruCache<String, Bitmap> mLruCache;

    public MemoryCacheUtil() {

        // maxMemory 是允许的最大值 ,超过这个最大值,则会回收
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;

        mLruCache = new LruCache<String, Bitmap>(cacheSize) {
            /**
             * 计算每张图片的大小
             * @param key
             * @param bitmap
             * @return
             */
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };

    }

    /**
     * 通过url从内存中获取图片
     *
     * @param url
     */
    public Bitmap getBitmapFromMemory(String url) {
        return mLruCache.get(url);
    }

    /**
     * 设置Bitmap到内存
     *
     * @param url
     * @param bitmap
     */
    public void setBitmapToMemory(String url, Bitmap bitmap) {
        if (getBitmapFromMemory(url) == null) {
            mLruCache.put(url, bitmap); // 设置图片
        }
    }

    /**
     * 从缓存中删除指定的Bitmap
     *
     * @param key
     */
    public void removeBitmapFromMemory(String key) {
        mLruCache.remove(key);
    }

}
  • 使用对应的键和值将数据put进集合中,使用get方法再取出来

    2、本地缓存:File

    将网络上获取下来的数据,保存在本地文件中.



import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by lenovo on 2018/5/18.
 */

public class LocalCacheUtil {
    private String cachePath;

    public LocalCacheUtil(Context context, String uniqueName) {
        cachePath = getCacheDirString(context, uniqueName);
    }


    /**
     * 设置Bitmap数据到本地
     *
     * @param url
     * @param bitmap
     */
    public void setBitmapToLocal(String url, Bitmap bitmap) {
        FileOutputStream fos = null;
        try {
            String fileName = MD5Encoder.encode(url);
            File file = new File(cachePath, fileName);
            File parentFile = file.getParentFile();//获取上级所有目录
            if (!parentFile.exists()) {
                // 如果文件不存在,则创建文件夹
                parentFile.mkdirs();
            }
            fos = new FileOutputStream(file);
            // 将图片压缩到本地
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (fos != null) {
                try {
                    fos.close();//关闭流
                    fos = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 通过url获取Bitmap
     *
     * @param url
     */
    public Bitmap getBitmapFromLocal(String url) {
        try {
            File file = new File(cachePath, MD5Encoder.encode(url));
            if (file.exists()) {
                // 如果文件存在
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream (file));
                //Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                return bitmap;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*获取缓存目录的路径:String类型*/
    private String getCacheDirString(Context context, String uniqueName) {
        File file = null;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            file = new File(context.getExternalCacheDir(), uniqueName);
            //file = new File(Environment.getExternalStorageDirectory(), uniqueName);
        } else {
            file = new File (context.getCacheDir(), uniqueName);
        }

        if (!file.exists()) {
            file.mkdirs();
        }

        return file.getAbsolutePath();
    }
}

MD5工具类:

用于生成保存的文件名:



import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by lenovo on 2018/5/18.
 */

public class MD5Encoder {
    public static String encode(String pwd) {
        StringBuffer sb = new StringBuffer();
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(pwd.getBytes("UTF-8"));
            for (int i = 0; i < bytes.length; i++) {
                String s = Integer.toHexString(0xff & bytes[i]);

                if (s.length() == 1) {
                    sb.append("0" + s);
                } else {
                    sb.append(s);
                }
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
}


3、网络缓存:AsyncTask异步请求

 通过url从网络上获取资源数据,通过异步AsyncTask拉取数据,同时,如果成功拉取到数据,将数据保存在本地和内存中一份。

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by lenovo on 2018/5/18.
 */

public class NetCacheUtil {
    private final MemoryCacheUtil mMemoryCacheUtil;
    private LocalCacheUtil mLocalCacheUtil;

    public NetCacheUtil(MemoryCacheUtil mMemoryCacheUtil, LocalCacheUtil mLocalCacheUtil) {
        this.mMemoryCacheUtil = mMemoryCacheUtil;
        this.mLocalCacheUtil = mLocalCacheUtil;
    }

    /**
     * 获取服务端数据
     *
     * @param ivPhoto
     * @param url
     */
    public void getBitmapFromInternet(ImageView ivPhoto, String url) {

        new BitmapAsyncTask().execute(ivPhoto, url); // 开启AsyncTask

    }

    /**
     * 第一个泛型:参数类型  第二个泛型:更新进度的泛型, 第三个泛型是OnPostExecute的结果
     * Object : 传入的参数类型
     * Void : 进度
     * Bitmap : 返回类型
     */
    private class BitmapAsyncTask extends AsyncTask<Object, Void, Bitmap> {

        private ImageView ivPhoto;
        private String url;

        /**
         * 运行在子线程中:请求数据
         *
         * @param params
         * @return
         */
        @Override
        protected Bitmap doInBackground(Object... params) {

            ivPhoto = (ImageView) params[0]; // 获取两个参数
            url = (String) params[1];

            Bitmap bitmap = downloadBitmap(url); // 从网络上加载图片

            return bitmap;
        }

        /**
         * 在主线程中执行 用于更新界面
         *
         * @param bitmap
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);

            if (bitmap != null) {

                ivPhoto.setImageBitmap(bitmap); // 为ImageView设置图片

                System.out.println("从网络获取图片...");

                // 将获取到的图片加载到本地
                mLocalCacheUtil.setBitmapToLocal(url, bitmap);
                // 将获取到的图片加载到内存
                mMemoryCacheUtil.setBitmapToMemory(url, bitmap);
            }

        }
    }

    /**
     * 根据url从网络上获取图片
     *
     * @param imageUrl 图片路径
     * @return
     */
    private Bitmap downloadBitmap(String imageUrl) {

        HttpURLConnection conn = null;

        try {
            URL url = new URL(imageUrl);
            conn = (HttpURLConnection) url.openConnection(); // 打开连接
            conn.setReadTimeout(5000); // 设置读取超时时间
            conn.setConnectTimeout(5000); // 设置连接超时时间
            conn.setRequestMethod("GET"); // 设置请求方式
            conn.connect(); // 开始连接

            if (conn.getResponseCode() == 200) {
                // 访问成功
                InputStream is = conn.getInputStream(); // 获取流数据
                // 对图片进行压缩处理
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2; // 图片的宽高都为原来的一半(分辨率)
                Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); // 将流数据转成Bitmap对象

                return bitmap;

            } else {
                // 访问失败
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect(); // 断开连接
            }
        }
        return null;
    }
}


MyBitmapUtil:集成使用

工具类:MyBitmapUtil:三级缓存的工具类

import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;

/**
 * Created by lenovo on 2018/5/18.
 */

public class MyBitmapUtil {
    private MemoryCacheUtil mMemoryCacheUtil = null; // 内存缓存(lrucache)
    private LocalCacheUtil mLocalCacheUtil = null; // 本地缓存(file)
    private NetCacheUtil mNetCacheUtil = null; // 网络缓存

    public MyBitmapUtil(Context context, String uniqueName) {
        mMemoryCacheUtil = new MemoryCacheUtil();
        mLocalCacheUtil = new LocalCacheUtil(context,uniqueName);
        mNetCacheUtil = new NetCacheUtil(mMemoryCacheUtil, mLocalCacheUtil);
    }

    /**
     * 将图片资源设置给控件
     *
     * @param url
     * @param ivPhoto
     */
    public void display(String url, ImageView ivPhoto) {

        Bitmap bitmap = null;

        // 1.判断内存中是否有缓存
        bitmap = mMemoryCacheUtil.getBitmapFromMemory(url); // 从内存中获取Bitmap
        if (bitmap != null) {
            ivPhoto.setImageBitmap(bitmap); // 设置图片
            System.out.println("从内存获取图片...");
            return;
        }
        // 2.判断本地是否有缓存
        bitmap = mLocalCacheUtil.getBitmapFromLocal(url); // 从本地缓存中获取Bitmap
        if (bitmap != null) {
            ivPhoto.setImageBitmap(bitmap); // 设置本地图片
            mMemoryCacheUtil.setBitmapToMemory(url, bitmap); // 设置图片到内存
            System.out.println("从本地获取图片...");
            return;
        }
        // 3.从网络获取数据

        mNetCacheUtil.getBitmapFromInternet(ivPhoto, url); // 设置图片

    }


}

MainActivity:在onCreate方法内
 
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);
        //获取控件
        iv = (ImageView) findViewById(R.id.img);
        //图片存放的地址
        String uniqueName = Environment.getExternalStorageDirectory()+".png";
        //实例化工具类
        MyBitmapUtil utils = new MyBitmapUtil(MainActivity.this,uniqueName);
        //设置网络请求的地址以及控件的ID
        utils.display ("http://up.qqya.com/allimg/2017-p02/17-122808_2167.jpg",iv);

    }


    参考博客地址:https://blog.csdn.net/sinat_31057219/article/details/71191962      点击打开链接


  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: c是一种编程语言,它是由丹尼斯·奇在1972年发明的。c语言是一种高级的、面向过程的编程语言,被广泛应用于系统编程。它拥有简洁、高效的特点,被认为是编程语言中的一种经典。 c语言的语法简洁清晰,易于理解和学习。它提供了丰富的数据类型和操作符,可以灵活地进行变量的定义和使用。它还支持数组和指针的操作,使得程序的运行效率得到提高。与其他高级语言相比,c语言的执行效率更高。此外,c语言还提供了丰富的标准库和函数,可以方便地进行各种操作和处理。 c语言广泛应用于系统编程,如操作系统、嵌入式系统等方面。由于其高效的执行速度和灵活的特性,c语言被用于开发各种应用,如编译器、数据库、网络协议等。许多著名的软件和操作系统都是用c语言编写的,如Linux操作系统、MySQL数据库等。 与其他编程语言相比,c语言的学习和使用门槛较低,但也需要掌握一定的编程基础知识。掌握c语言的基本语法和常用的函数库,可以进行基本的编程操作。但要深入了解c语言的各种特性和底层原理,需要更多的学习和实践。 总之,c语言是一种经典的编程语言,具有简洁、高效、灵活的特点。无论是初学者还是有一定编程经验的开发者,都应学习和掌握c语言,以便能够应用于各种软件和系统的开发。 ### 回答2: " c "这个字母是拉丁字母表中的第三个字母,也是英语字母表中的第三个字母。在汉语拼音中,"c"代表着声母"ch"的发音,如"车"、"吃"等。此外,在数学中,"c"可以代表复数域中的复数,与实数相对应。 在计算机科学领域,"c"是一种编程语言的名称。C语言是一种通用的高级编程语言,广泛应用于系统软件开发、嵌入式系统以及其他一些需要高效性能的领域。C语言的设计简洁且灵活,适合编写底层的系统代码。 除此之外,"C"还有其他含义。在天气预报中,"C"表示摄氏度的单位。在航空中,"C"表示航空公司的代码,用于标识航空公司。在音乐领域,"C"是音乐的一个音符,也是一个音的名称。在化学中,"C"代表着一种化学元素,即碳。 总而言之,"c"是一个广泛使用的字母,具有多个意义和用途。无论是在语言、科学、技术还是其他领域,它都扮演着重要的角色。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值