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

一.三级缓存概述

(一)三级缓存的三级

第一级是内存,最快,不需要网络

第二级是本地,不需要网络

第三级是网络,需要网络请求

三级缓存机制的思想:

如果在内存中获取到数据,就不去本地和网络中获取。

如果在本地中获取到数据就不去网络中获取,

如果内存和本地中不存在数据,就要去网络中请求数据

三级缓存技术能有效节省用户的流量,但是也会增加一些内存负担。

二.使用示例展示三级缓存工具栏类的使用

程序运行后的页面:

虽然只用一个按钮和一个图片显示,但是通过测试(联网状态和断网状态对比)能知道图片是从网络中获取还是从本地或者中内存。

这里用到了几个其他自己编程的小工具类。

(一)添加手机权限,网络权限和SD卡写的权限

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

(二)编写布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="start"
        android:text="加载图片" />

    <ImageView
        android:id="@+id/main_iv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

</LinearLayout>

上面是一个非常简单的布局文件。

(三)设计一个方便调试显示个工具类

package com.lwz.threelevelt;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

/**
 * 本类用于简易的显示信息
 * 比如土司,或Log信息
 */

public class ShowUtils {
    //这里DEBUG的作用是,可以在程序完成后设置DEBUG的值为false,程序以后就不会在显示以前的打印信息
    public static  boolean DEBUG = true;

    //各种Log打印
    public static void e(Object o) {
        if (DEBUG)
            Log.e("TAG", "打印:------      " + o.toString());
    }

    public static void e(int i) {
        if (DEBUG)
            Log.e("TAG", "打印:------      " + i);
    }

    public static void e(float i) {
        if (DEBUG)
            Log.e("TAG", "打印:------      " + i);
    }

    public static void e(boolean b) {
        if (DEBUG)
            Log.e("TAG", "打印:------      " + b);
    }

    //各种土司
    public static void ts(Context context, Object object) {
        if (DEBUG)
            Toast.makeText(context, object + "", Toast.LENGTH_SHORT).show();
    }

    public static void tsl(Context context, Object object) {
        if (DEBUG)
            Toast.makeText(context, object + "", Toast.LENGTH_LONG).show();
    }

}

(四)文件操作的一个工具类

public class FileUtils {

    //判断是否本地有sd卡,确定是否保存在SD卡内
    String path;//文件存储的地方

    /**
     * 通过构造方法传入存储的路径
     */
    public FileUtils(Context context, String dirName) {
        //判断是否本地有sd卡,这里代表的是SD卡在就绪的状态
//这里判断相等状态要使用.equal,使用==会匹配不到???
        if (Environment.getExternalStorageState() .equal( Environment.MEDIA_MOUNTED)) {
            ShowUtils.e("SD卡就绪状态");
            path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + dirName;
        } else {
            ShowUtils.e("SD卡没有就绪状态");
            //保存在内部存储器中
            path = context.getCacheDir().getAbsolutePath() + "/" + dirName;
        }
        //创建文件
        new File(path).mkdirs();
    }

    /**
     * 文件的写入
     * 传入一个文件的名称和一个Bitmap对象
     * 最后的结果是保存一个图片
     */
    public void saveToSDCard(String key, Bitmap bmp) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(path, key));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //保存图片的设置
        bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
        try {
            fos.close();//关闭流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件的读取,
     * 根据文件的名字,读取出一个Bitmap的对象,
     * 如果之前保存过就有值,否则是null
     */
    public Bitmap readFromSDCard(String key) {
        return BitmapFactory.decodeFile(new File(path, key).getAbsolutePath());
}
}

(五)最最重要的三级缓存功能的实现的工具类

package com.lwz.threelevelt;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.util.LruCache;

import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.lwz.threelevelt.ShowUtils.e;

/**
 * 图片三级缓存机制的实现类
 * <p>
 * 首先是内存,最快
 * 其次是本地,不需要网络
 * 最后是网络,需要网络请求
 * 这如果在内存中获取到数据,就不去本地和网络中获取,同样如果在本地中获取到数据就不去网络中获取,
 * 如果内存和本地中不存在数据,采取网络中请求数据
 * <p>
 * ,这里结合的是另一个fileUtils的工具类来实现
 * <p>
 * 调用方法也是很简单的:
 */

public class ImageLoader {

    //下载使用的线程池对象
    ExecutorService threadLooper;
    //缓存类,能过获取和写入数据到缓存中,短时间的存储!!
    private static LruCache<String, Bitmap> cache;
    //文件操作类对象
    private FileUtils fileUtils;

    /**
     * 构造方法,需要传入一个保存文件的名字
     * 实例化:线程池对象,缓存类,文件操作类对象
     */
    public ImageLoader(Context context, String dirName) {
        //获取系统分配的最大内存
        int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
        //实例化缓存类的对象
        cache = new LruCache<String, Bitmap>(maxSize) {
            //每一个键所对应的值的大小
            //自动释放低频率的文件
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
        fileUtils = new FileUtils(context, dirName);//实例化文件操作类的对象
        threadLooper = Executors.newFixedThreadPool(5);//实例化线程池,并设置运行的最大线程数
    }

    /**
     * 下载图片的方法,这也是提供给我们调用的方法
     * 需要传入一个URL地址,和一个图片下载成功后的回调方法
     */
    public void loadImage(final String url, @NonNull final ImageLoadListener listener) {
        //去掉所有需要转义的斜杠,把获得的字符串作为Bitmap对象的一个标示符,通过它和它的Bitmap对象一一对应
        final String key = url.replaceAll("[\\W]", "");
        //第一级,先判断缓存类中是否有数据
        if (readFromCache(key) != null) {
            //直接拿出来
            e("从缓存中加载");
            listener.loadImage(readFromCache(key));
        } else {
            //第二级,再判断本地中是否存在数据
            final Bitmap bitmap = fileUtils.readFromSDCard(key);//查看是否存在数据
            if (bitmap != null) {//本地中存在数据
                //存储到缓存
                e("从SDCard中加载");
                saveToCache(key, bitmap);//把从SD卡中读取到的数据保存到缓存中,
                //返回
                listener.loadImage(fileUtils.readFromSDCard(key));//返回一个数据给调用者
            } else {
                //第三级,从网络中下载数据
                //要把数据分别存入本地中内存中

                //下载,使用子线程

                //创建一个Handler对象,这里还是主线程,
                // 下载是在子线程,然后调用Handler对象发送数据给主线程
                final Handler handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        e("从网络下载");
                        listener.loadImage((Bitmap) msg.obj);
                    }
                };

                //线程池的使用,在里面下载数据
                threadLooper.execute(new Runnable() {
                    @Override
                    public void run() {
                        //开始下载
                        try {
                            URL u = new URL(url);
                            InputStream inputStream = u.openStream();
                            Bitmap bitmap1 = BitmapFactory.decodeStream(inputStream);//获取bitmap对象
                            fileUtils.saveToSDCard(key, bitmap1);//保存文件到SD卡
                            saveToCache(key, bitmap1);//保存文件到内存中
                            //使用Handler对象给主线程发送消息
                            Message msg = handler.obtainMessage();
                            msg.obj = bitmap1;
                            handler.sendMessage(msg);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }

    }

    /**
     * 取消子线程的任务
     */
    public void cancelDownLoad() {
        threadLooper.shutdown();
    }

    /**
     * 定义一个接口,里面有一个方法,
     * 这里有一个Bitmap对象参数,作用是让调用这接收这个Bitmap对象,实际这bitmap对象就是缓存中的对象
     */
    public interface ImageLoadListener {
        public void loadImage(Bitmap bmp);
    }

    /**
     * 使用缓存类存储Bitmap对象
     */
    private void saveToCache(String key, Bitmap bmp) {
        cache.put(key, bmp);
    }

    /**
     * 使用缓存类获取Bitmap对象
     */
    private Bitmap readFromCache(String key) {
        return cache.get(key);
    }
}

上面代码中e(“XXX”) 和ShowUtils.e(“XXX”)效果是一样的,因为导入方式是静态的:import static com.lwz.threelevelt.ShowUtils.e;所以可以省略类名ShowUtils。

有些简单的方法这样使用是非常方便的。

(六)最后的调用类

package com.lwz.threelevelt;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

/**
 * 三级缓存工具类的调用测试
 * 1.创建三级缓存类的对象,传入上下文和图片名字
 * 2.调用三级缓存类的对象的loadImage方法,
 * 传入两个参数,第一个参数是URL地址,第二个参数是回调接口,在回调接口内可以接收到根据URL地址下载到的Bitmap对象
 */
public class MainActivity extends AppCompatActivity {

    //定义布局的控件
    ImageView imageView;
    //定义三级缓存工具类
    ImageLoader loader;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ShowUtils.DEBUG = false;
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.main_iv);
        loader = new ImageLoader(this, "test3");//创建文件夹
    }

    public void start(View v) {
        loader.loadImage("http://p3.so.qhmsg.com/bdr/326__/t018da60b972e086a1d.jpg", new ImageLoader.ImageLoadListener() {
            @Override
            public void loadImage(Bitmap bmp) {
                imageView.setImageBitmap(bmp);
            }
        });
    }
}

可以看到实现了三级缓存的工具类后,调用还是非常简单的。

程序运行后显示的界面:

第一次点击按钮,页面显示:

显示的Log数据:

可以看到数据是从网络中获取到的。因为刚刚开始本地或缓存中都没有数据。

第二次或多次点击按钮,显示的Log数据:

可以看到数据是从缓存中获取到的。因为图片的数据每次打开后,缓存中都会有它的数据。

退出程序后,再进入程序点击按钮,显示的Log数据:

可以看到数据是从本地中获取到的。因为缓存中的数据很容易被回收,本地的数据不会被回收。

卸载程序后,再安装程序点击按钮,显示的Log数据:

可以看到数据是从SD卡中获取到的。因为程序存放的数据是在SD卡下的,程序卸载后数据依然存在,但是如果数据保存在内部存储器,卸载程序也会删除数据。

上面就是三级缓存图片工具类的实现和三级缓存图片的一个简单使用。

时间: 2024-11-25 08:11:45

Android三级缓存机制工具类的实现的相关文章

Android开源框架ImageLoader:加载图片的三级缓存机制

前言:可从  https://github.com/nostra13/Android-Universal-Image-Loader 下载三级缓存机制的开源框架.下文简单介绍该框架中主要的常用方法,掌握这些方法,基本就可应对多数图片下载的需求. 注意:以下代码为示意代码片断,仔细读一下应能知道怎么用.蓝色表示为开源框架中的类. 1.初始化ImageLoader类对象: ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.

郑州尚学堂:php实现网页缓存的工具类分享

PHP是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域.PHP 独特的语法混合了C.Java.Perl以及PHP自创的语法.它可以比CGI或者Perl更快速地执行动态网页.今天给大家分享php实现网页缓存的工具类的代码及使用方法,非常的实用,希望可以对有需求的小伙伴带来帮助. php程序在抵抗大流量访问的时候动态网站往往都是难以招架,所以要引入缓存机制,一般情况下有两种类型缓存 一.文件缓存 二.数据查询结果缓存,使用内存来实现高速

Android开源项目大全 - 工具类

主要包括那些不错的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多媒体相关及其他. 一.依赖注入DI 通过依赖注入减少View.服务.资源简化初始化,事件绑定等重复繁琐工作 AndroidAnnotations(Code Diet)android快速开发框架 项目地址:https://github.com/excilys/androidannotations 文档介绍:https://github.com/excilys

Android开发调试日志工具类[支持保存到SD卡]

直接上代码: package com.example.callstatus; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.UnknownHostException; import java.text.SimpleDateFormat; impor

单服务缓存redis工具类

import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; /** * 单服务缓存redis工具类(需要额外jar包jedis) */ public class RedisSingletonPool { private static String ip = ConfigUtil.readConfigForObject("SIN

Android调节屏幕亮度工具类BrightnessUtils

如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 项目需要做了一个调节屏幕的工具类 /* * Android调节屏幕亮度工具类 * by itas109 * http://blog.csdn.net/itas109 * * 注意:需要添加setting权限 * <uses-permission android:name="android.permission.WRITE_SETTINGS" /> */ publ

wemall app商城源码android开发MD5加密工具类

wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享android开发MD5加密工具类主要代码,供技术员参考学习. package com.gzcivil.utils; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgori

Android中常用的工具类01

1.图片和视频缩略图工具类 import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.ThumbnailUtils; /** * 缩略图生成工具类 * @author * */ public class ThumbnailGenerateUtils { private ThumbnailGenerateUtils(){}; /** * 根据指定的图像路径和大小来获取缩略图

Android中常用的工具类02

1.读取手机联系人信息 一般用在读取手机通讯录上传,这一块比较多. import java.util.ArrayList; import java.util.List; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.provider.ContactsContract.CommonDataKinds.Phon