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

图片的三级缓存

一、概述

一開始在学习Android的时候。处理图片的时候,每次获取图片都是直接从网络上面载入图片。

可是在开发项目的过程中,每次点击进入app里面,图片都要慢慢的再一次从网络上面载入。

给用户的体验很不好,第一个等待的时间很令人dan 疼

第二个给用户的流量造成了不必要的浪费

因此提出图片的三级缓存策略,

所谓的三级缓存:就是在手机载入图片的时候,

1、首先从内存中载入,

2、假设内存中没有的话,从sd卡上获取。读取到之后将图片写入到内存中

3、假设sd卡上没有的话,从网络上获取,从网络上获取之后。写入到sd卡上,还有内存中

整体效果图例如以下:(比較模糊了点将就看吧)

想要写一个网络载入的框架,首先来写一个内存缓存的类:

package com.jishihuitong.bitmaputil;

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

/**
 * Created by hss on 2016/8/4.
 * <p>图片内存缓存工具类
 */
public class MemoryCache {

    /**
     * 内存缓存对象
     */
    private LruCache<String, Bitmap> mLruCache;
    private static final String TAG = "MemoryCache";

    /**
     * 构造方法,初始化LruCache
     */
    public MemoryCache() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheMemory = maxMemory/8;
        mLruCache = new LruCache<String,Bitmap>(cacheMemory){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                LogUtil.d(TAG,"创建了lruCache实例");
                return value.getByteCount();
            }
        };

    }

    /**
     * 从内存中获取Bitmap对象
     * @param url key
     * @return bitmap对象
     */
    public Bitmap getBitmap(String url){
        LogUtil.d(TAG,"从内存中获取图片"+url);
        return mLruCache.get(url);
    }

    /**
     * 将图片对象依照键值对保存在内存中
     * @param url key
     * @param bitmap value
      */
    public void putBitmap(String url,Bitmap bitmap){
        LogUtil.d(TAG,"将图片保存到内存中"+url);
        mLruCache.put(url,bitmap);
    }
}

然后来写 本地缓存 实例,本地通常是保存在了sd卡上面,

可是有时候须要推断一下手机有没有sd卡。要是没有的话我们就把他存在缓存文件夹里面,

将文件写入到本地的话。为了安全操作。我们须要对文件名称进行一次MD5加密,

这里是MD5的代码

package com.jishihuitong.bitmaputil;

import java.security.MessageDigest;

/**
 * Created by hss on 2016/8/4.
 */
public class MD5Encoder {
    public static String encode(String string) throws Exception {
        byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }
}

将图片保存到本地的代码

package com.jishihuitong.bitmaputil;

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.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * Created by hss on 2016/8/4.
 */
public class SDCardCache {
    private static final String TAG = "SDCardCache";
    private static String sdcardCache;

    /**
     * 从sd卡缓存中获取图片对象
     *
     * @param url 图片URL
     * @return bitmap
     */
    public Bitmap getSDCardBitmap(Context context,String url) {
        Bitmap bitmap;
        String filename = null;
        try {
            filename = MD5Encoder.encode(url);
            LogUtil.d(TAG,"获取到文件的filename = "+filename);
            bitmap = BitmapFactory.decodeStream(new FileInputStream(new File(getSDCache(context), filename)));
            if (bitmap != null) {
                LogUtil.d(TAG,"从SD卡中获取到图片"+url);
                return bitmap;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }

    /**
     * 将图片缓存到本sd卡
     * @param context 上下文对象
     * @param url 图片URL
     * @param bitmap 图片对象
     */
    public  void saveBitmapToSDCard(Context context,String url, Bitmap bitmap) {
        String filename = null;
        try {
            filename = MD5Encoder.encode(url);
            LogUtil.d(TAG,"保存的图片的filename = "+filename);
        } catch (Exception e) {
            e.printStackTrace();
        }
        File file = new File(getSDCache(context), filename);
        File parentFile = file.getParentFile();
        if(!parentFile.exists()){
            LogUtil.d(TAG,"父文件夹不存在,创建");
            parentFile.mkdirs();
        }
        //将图片保存到本地
        try {
            LogUtil.d(TAG,"文件保存在了本地");
            //将图片保存到本地的方法,第一个參数 图片的格式,第二个參数图片的质量100表示最高质量
            //第三个參数 图片的流
            bitmap.compress(Bitmap.CompressFormat.PNG, 100,new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取到图片在本地缓存的文件夹
     * @param context 上下文对象
     * @return 缓存文件夹的字符串
     */
    public  String getSDCache(Context context){
        String state = Environment.getExternalStorageState();
        if(state==Environment.MEDIA_MOUNTED){
            LogUtil.d(TAG,"SD卡挂载,获取到sd卡文件夹");
            sdcardCache = Environment.getExternalStorageDirectory() + "cache/";
        }else{
            LogUtil.d(TAG,"SD卡不在,获取到cache文件夹");
            sdcardCache = context.getCacheDir().getAbsolutePath();
            LogUtil.d(TAG,"sdcardCache = "+sdcardCache);
        }
        return sdcardCache;
    }
}

然后说从网络上获取图片的操作,这个操作涉及到訪问网络,这个样例里面使用的是asyncTask

在asyncTask里面,须要有在UI线程的回调,将图片对象设置到控件中,所以也须要一个ImageView对象的引用

把图片下载下来之后,还要将图片保存到本地 还有内存中,

所以该网络缓存的工具类中,须要有ImageView的引用,还有本地和内存的缓存对象

我们将他们在构造方法中进行实现

訪问网络的工具类:

package com.jishihuitong.bitmaputil;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;

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

/**
 * Created by hss on 2016/8/4.
 */
public class NetCacheUtil {
    /**
     * 内存缓存对象
     */
    private MemoryCache mMemoryCache;
    /**
     * 本地缓存对象
     */
    private SDCardCache mSDCardCache;
    /**
     * ImageView对象的引用
     */
    public ImageView mImageView;
    /**
     * 全局使用的URL
     */
    public String mUrl;
    /**
     * 上下文对象
     */
    public Context context;
    public Handler handler = new Handler();
    public static final String TAG = "NetCacheUtil";

    public NetCacheUtil(Context context,MemoryCache memoryCache, SDCardCache sdcardCache) {
        this.mMemoryCache = memoryCache;
        this.mSDCardCache = sdcardCache;
        this.context = context;
    }
    public void getBitmapFromNet(ImageView view,String url){
        //开启一个任务去下载图片,并设置到view上
        new BitmapAsyncTask().execute(view,url);
    }

    /**
     * 下载图片的多线程任务
     * <p/>
     * 第一个泛型 : 參数类型  相应doInBackground()
     * 第二个泛型 : 更新进度   相应onProgressUpdate()
     * 第三个泛型 : 返回结果result   相应onPostExecute
     */
    class BitmapAsyncTask extends AsyncTask<Object,Void,Bitmap>{

        @Override
        protected Bitmap doInBackground(Object... params) {
            //获取到參数
            mImageView = (ImageView) params[0];
            mUrl = (String) params[1];
            //在主线程运行,让ImageView和URL进行绑定,防止图片错乱
            handler.post(new Runnable() {
                @Override
                public void run() {
                    mImageView.setTag(mUrl);
                }
            });
            //运行下载图片的任务
            Bitmap bitmap = downloadBitmap(mUrl);
            return bitmap;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //该方法将在DoInBackground之后运行,
            if(bitmap!=null){
                String url = (String) mImageView.getTag();
                //防止图片错乱。对刚才的图片URL和如今获取到的URL进行比对
                if(url.equals(mUrl)){
                    mImageView.setImageBitmap(bitmap);
                    LogUtil.d(TAG,"将图片保存在本地和内存中" +context +"      "+mUrl+"      "+bitmap  );
                    mSDCardCache.saveBitmapToSDCard(context, mUrl, bitmap);
                    mMemoryCache.putBitmap(mUrl, bitmap);
                }
            }
        }
    }

    /**
     * 链接网络下载图片,下载下来之后将图片保存在本地和内存中
     * @param mUrl
     * @return
     */
    private Bitmap downloadBitmap(String mUrl) {
        HttpURLConnection conn =null;
        try {
            conn = (HttpURLConnection) new URL(mUrl).openConnection();
            conn.setReadTimeout(5 * 1000);
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            conn.connect();
            int code = conn.getResponseCode();
            if(code == 200){
                InputStream inputStream = conn.getInputStream();
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                return bitmap;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(conn!=null){
                conn.disconnect();
                conn = null;
            }
        }
        return null;
    }
}

三个阶段的缓存工具类都准备好了,接下来定义一个类。一个方法,能够让他们一次性运行,那么这种方法就是display方法

package com.jishihuitong.bitmaputil;

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

/**
 * Created by hss on 2016/8/4.
 * <p/>
 * 综合使用三级缓存的工具类
 */
public class BitmapUtil {

    /**
     * 内存缓存类
     */
    private final MemoryCache memoryCache;
    /**
     * SD卡缓存类
     */
    private final SDCardCache sdCardCache;
    /**
     * 网络缓存类
     */
    private final NetCacheUtil netCacheUtil;
    /**
     * 上下文对象
     */
    private Context context;

    public BitmapUtil(Context context) {
        memoryCache = new MemoryCache();
        sdCardCache = new SDCardCache();
        netCacheUtil = new NetCacheUtil(context, memoryCache, sdCardCache);
        this.context = context;
    }

    /**
     * 调用该方法能够进入图片三级缓存流程。将图片展示在UI界面上
     * @param view ImageView对象
     * @param url 图片訪问路径
     */
    public void display(ImageView view,String url){
        //根URL从内存中获取。
        Bitmap memoryCacheBitmap = memoryCache.getBitmap(url);
        //假设获取到的不为空,将图片设置到控件上。返回
        if(memoryCacheBitmap!=null){
            view.setImageBitmap(memoryCacheBitmap);
            return;
        }
        //假设内存获取为空的话,再依据URL从内存中获取,
        Bitmap sdCardBitmap = sdCardCache.getSDCardBitmap(context, url);
        //假设不为空,将图片设置给控件。然后将图片保存在内存中 返回
        if(sdCardBitmap!=null){
            view.setImageBitmap(sdCardBitmap);
            memoryCache.putBitmap(url,sdCardBitmap);
            return;
        }
        //本地也没有的话就仅仅能到网络去找了,该方法里有直接将图片设置给控件的方法
        netCacheUtil.getBitmapFromNet(view,url);
    }
}

工具类都写好之后,我们在一个activity里面简单的调用一下,

首先整个布局就是一个ListView。然后里面的item就是一个ImageView

布局代码例如以下

main_activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/lv">

    </ListView>
</LinearLayout>

view_listview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:orientation="vertical"
    >
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:id="@+id/iv"
        android:src="@mipmap/empty_photo"
        />

</LinearLayout>

在MainActivity里面调用方法 让图片填充ListView

package com.jishihuitong.bitmaputil;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.media.Image;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ArrayList<String> list;
    private BitmapUtil bitmapUtil;
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //准备数据
        list = new ArrayList<String>();
        list.add("http://192.168.0.11:8080/images/10001.png");
        list.add("http://192.168.0.11:8080/images/10002.png");
        list.add("http://192.168.0.11:8080/images/10003.png");
        list.add("http://192.168.0.11:8080/images/10004.png");
        list.add("http://192.168.0.11:8080/images/10005.png");
        list.add("http://192.168.0.11:8080/images/10006.png");
        //初始化图片缓存工具类
        bitmapUtil = new BitmapUtil(this);
        ListView lv = (ListView) findViewById(R.id.lv);
        lv.setAdapter(new MyAdapter(getApplication(),0,list));
    }

    class MyAdapter extends ArrayAdapter {

        public MyAdapter(Context context, int resource, List<String> list) {
            super(context, resource, list);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view;
            ImageView iv;
            if (convertView == null) {
                view = View.inflate(getApplicationContext(), R.layout.view_listview, null);
            } else {
                view = convertView;
            }
            iv = (ImageView) view.findViewById(R.id.iv);
            iv.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.empty_photo));
            //为了防止图片乱跳。给imageView设置标记
            iv.setTag(list.get(position));
            LogUtil.d(TAG, "訪问的URL为 " + list.get(position));
            //载入图片
            bitmapUtil.display(iv, list.get(position));
            return view;
        }

    }
}

部署在tomcat上面的图片路径如图所看到的:

点击下载代码

原文地址:https://www.cnblogs.com/llguanli/p/8521398.html

时间: 2024-10-19 19:20:56

Android进阶图片处理之三级缓存方案的相关文章

浅谈图片载入的三级缓存(一)

之前被人问及过.图片的三级缓存是什么啊,来给我讲讲,图片三级缓存,好高大尚的名字,听着挺厉害,应该是非常厉害的技术.当时不会啊,也就没什么了.没有说出来呗,前一阶端用到了BitmapUtils的图片缓存框架,索性就自己找些知识点来研究一些图片的三级缓存是什么吧.真所谓是知识你要是不知道,那就真的说不出所以然来.可是当你真正的去了解了.三级缓存也不是那么高端的技术. 好了,闲话不多说了.開始图片的三级缓存原理吧. 什么是图片的三级缓存 1.内存缓存 优先载入,速度最快 2.本地缓存 次优先载入 速

Android 平滑图片加载和缓存库 Glide 使用详解

在图片加载库烂大街的今天,选择一个适合自己使用的图片加载库已经成为了每一个Android开发者的必经之路.现在市面上知名的图片加载库有UIL,Picasso,Volley ImageLoader,Fresco以及我们今天的主角Glide.它们各有千秋,不能评定谁一定比谁好,只能说哪一个更适合你. 我的理解 下面我来谈一下个人对这些图片加载库的理解,如有错误,还望指教. Universal Image Loader:一个强大的图片加载库,包含各种各样的配置,最老牌,使用也最广泛. Picasso:

Android远程图片获取和本地缓存

对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对 应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用消耗掉了他手机流量的话,那么可想而知你的应用将面 临什么样的命运. AD:2014WOT全球软件技术峰会北京站 课程视频发布 另外一个问题就是加载速度,如果应用中图片加载速度很慢的话,那么用户同样会等到崩溃. 那么如何处理好图片资源的获取和管理呢? 异步下载 本地缓存 异步下载 大家都知道,在and

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

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

移动应用开发(IOS/android等)中一个通用的图片缓存方案讲解(附流程图)

在移动应用开发中,我们经常会遇到从网络请求图片到设备上展示的场景. 如果每次都重复发起请求,浪费流量.浪费电量,用户体验也不佳: 将图片持久化到磁盘也不失为一种策略:但每次从文件读取图片也存在一定的io开销,就算采用此策略,我们也需要控制磁盘缓存的容量,以免占用过多系统资源. 其实没有一个方案可以说是完美的方案,只有最适合自己业务需求的方案,才可以说是一个好方案. 我们下面所讲解的方案具备很强的通用性,设计思路简单而清晰: 1.假设每个网络图片的url具有唯一性,如果网络上的图片变化了,会引起输

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

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

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

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

Android中图片的三级缓存策略

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