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

【Android进阶图片处理之三级缓存方案】宁可枝头抱香死,何曾吹落北风中。这篇文章主要讲述Android进阶图片处理之三级缓存方案相关的知识,希望能为你提供帮助。
图片的三级缓存一、概述
            一開始在学习android的时候。处理图片的时候,每次获取图片都是直接从网络上面载入图片。
可是在开发项目的过程中,每次点击进入app里面,图片都要慢慢的再一次从网络上面载入。
给用户的体验很不好,第一个等待的时间很令人dan 疼
第二个给用户的流量造成了不必要的浪费
因此提出图片的三级缓存策略,
所谓的三级缓存:就是在手机载入图片的时候,
1、首先从内存中载入,
2、假设内存中没有的话,从sd卡上获取。读取到之后将图片写入到内存中
3、假设sd卡上没有的话,从网络上获取,从网络上获取之后。写入到sd卡上,还有内存中
整体效果图例如以下:(比較模糊了点将就看吧)

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

文章图片

想要写一个网络载入的框架,首先来写一个内存缓存的类:
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 图片对象 */ publicvoid 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 缓存文件夹的字符串 */ publicString 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="https://www.songbingjia.com/android/@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进阶图片处理之三级缓存方案

文章图片

点击下载代码

    推荐阅读