图片的三级缓存

一、概述

一開始在学习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上面的图片路径如图所看到的:

点击下载代码

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

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

    在图片加载库烂大街的今天,选择一个适合自己使用的图片加载库已经成为了每一个Android开发者的必经之路.现在市面上知名的图片加载库有UIL,Picasso,Volley ImageLoader,Fr ...

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

    对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对 应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用 ...

  3. 关于Android圆形图片的一种优化方案(可以显示网络图片)

    在Android App中,我们经常看到圆形头像图片,然后网上也有很多开源的控件.刚好这个项目用到了,也去找了一些开源的,发现并不完美,所以只好自己优化了,废话不多说,先上效果图: 下面是源码:本人能 ...

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

    我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存 ...

  5. 【Android - 进阶】之图片三级缓存的原理及实现

    在Android开发中,如果图片过多,而我们又没有对图片进行有效的缓存,就很容易导致OOM(Out Of Memory)错误.因此,图片的缓存是非常重要的,尤其是对图片非常多的应用.现在很多框架都做了 ...

  6. Android 的图片异步请求加三级缓存 ACE

    使用xUtils等框架是很方便,但今天要用代码实现bitmapUtils 的功能,很简单, 1 AsyncTask请求一张图片 ####AsyncTask #####AsyncTask是线程池+han ...

  7. Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference))

    因为之前项目同事使用了图片三级缓存,今天整理项目的时候发现同事还是使用了软引用(SoftRefrerence)和弱引用(WeakReference),来管理在内存中的缓存.看到这个我就感觉不对了.脑海 ...

  8. Android中图片的三级缓存策略

    在开发过程中,经常会碰到进行请求大量的网络图片的样例.假设处理的不好.非常easy造成oom.对于避免oom的方法,无非就是进行图片的压缩.及时的回收不用的图片.这些看似简单可是处理起来事实上涉及的知 ...

  9. Android中图片的三级缓存

    为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi ...

随机推荐

  1. java session cookie的使用

    Cookie; Session; URL重写; cookie在J2EE项目中的使用,Java中把Cookie封装成了java.servlet.http.Cookie类.每个Cookie都是该Cooki ...

  2. 【详●析】[GXOI/GZOI2019]逼死强迫症

    [详●析][GXOI/GZOI2019]逼死强迫症 脑子不够用了... [题目大意] 在\(2\times N\)的方格中用\(N-1\)块\(2\times 1\)的方砖和\(2\)块\(1\tim ...

  3. SQL语句新建数据库

    CREATE DATABASE 语句. CREATE DATABASE Epiphany ON ( NAME = Epiphany, FILENAME = 'E:\SQL SERVER 2008\Ep ...

  4. InnoDB INFORMATION_SCHEMA Metrics Table

    InnoDB INFORMATION_SCHEMA Metrics Table INNODB_METRICS表将所有InnoDB性能和资源相关计数器合并到一个INFORMATION_SCHEMA表中. ...

  5. vue App.vue router 过渡效果, keep-alive 结合使用示例

    1, router.js配置 每个路由的index值 2, router.js配置 每个路由的keepAlive值 app.vue 代码 <template> <div id=&qu ...

  6. Restful API(转)

    原文链接 http://www.ruanyifeng.com/blog/2014/05/restful_api.html 一.协议 API与用户的通信协议,总是使用HTTPs协议. 二.域名 应该尽量 ...

  7. POJ 2251-Dungeon Master (三维空间求最短路径)

    Description You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is co ...

  8. Google JavaScript代码风格指南

    Google JavaScript代码风格指南 修正版本 2.28 Aaron Whyte Bob Jervis Dan Pupius Eric Arvidsson Fritz Schneider R ...

  9. PTA 03-树2 List Leaves (25分)

    题目地址 https://pta.patest.cn/pta/test/16/exam/4/question/666 5-4 List Leaves   (25分) Given a tree, you ...

  10. [Vijos1512] SuperBrother打鼹鼠 (二维树状数组)

    传送门 直接搞就行. 注意下表re从零开始,而树状数组搞不了0,所以统一增加一个偏移量1. (话说数据随机是什么鬼?) # include <iostream> # include < ...