安卓图片三级缓存的集装类
来源:互联网 发布:淘宝直播达人合作 编辑:程序博客网 时间:2024/06/09 04:17
图片的三级缓存相信大家都了解过了,废话不多说,直接入主题:
缓存最主要的类:
package com.text.imgcachetext;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.os.Build;import android.os.Environment;import android.os.Handler;import android.os.SystemClock;import android.telephony.TelephonyManager;import android.text.TextUtils;import android.util.Log;import android.util.LruCache;import android.widget.ImageView;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.net.HttpURLConnection;import java.net.URL;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * @项目名: ImgCacheText * @包名: com.text.imgcachetext * @文件名: ImageCacheUtils * @创建者: wxr * @创建时间: 2016/11/2 13:59 * @描述: 图片缓存的工具类,虚拟内存缓存为可用内存的1/4,磁盘缓存为30M,网络连接失败默认3次重连 * @使用方式: 简单的链式调用ImageCacheUtils.with().load(String).into(ImageView); */public class ImageCacheUtils { private static final String TAG = "ImageCacheUtils"; private static final String DISK_CACHE_SUBDIR = File.separator + "PictureCache";//磁盘缓存的文件夹名称 //没有网络连接 private static final int NETWORN_NONE = 0; //wifi连接 private static final int NETWORN_WIFI = 1; //手机网络数据连接类型 private static final int NETWORN_2G = 2; private static final int NETWORN_3G = 3; private static final int NETWORN_4G = 4; private static final int NETWORN_MOBILE = 5; private static ImageCacheUtils mUtils = null; private static int mBeats = 3;//重连的总次数 private Context mContext = null; private LruCache<String, Bitmap> mLruCache = null; private Resources mRes = null; private String mImgUrl = null;//图片地址 private Bitmap mBitmap = null; private Handler mHandler = null; private ExecutorService mCacheThread = null;//线程池 private File mDiskPath = null; private static int mLruSize = 4;//可用虚拟内存的大小 4-->1/4 private static long mDiskSize = 30 * 1024 * 1024;//磁盘缓存的大小,这里给30M private static boolean mIsLoadImg = true;//是否加载图片 /**构造函数*/ private ImageCacheUtils(Context context) { this.mContext = context; if (this.mRes == null) { this.mRes = context.getResources(); } int maxMemory = (int) (Runtime.getRuntime().maxMemory()); int cacheSize = maxMemory / this.mLruSize; this.mLruCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }; this.mHandler = new Handler(); this.mCacheThread = Executors.newCachedThreadPool(); this.mDiskPath = getDiskPath(); } //------------------>start 虚拟内存缓存方法<---------------- /*** * 读取缓存中的图片 * @return */ private Bitmap getMemoryBitmap(String url) { String fileName = this.getFileMD5(url); Bitmap bitmap = mLruCache.get(fileName); return bitmap != null ? bitmap : null; } /** * 缓存中存储图片 * @param bitmap */ private void addMemoryBitmap(Bitmap bitmap, String url) { if (getMemoryBitmap(url) == null) { mLruCache.put(this.getFileMD5(url), bitmap); } } //------------------>end 虚拟内存缓存方法<------------------ //------------------>start 磁盘图片存储和获取<---------------- /**获取磁盘中的图片*/ private Bitmap getDiskCacheBitmap(String url) { try { File file = new File(mDiskPath, DISK_CACHE_SUBDIR); File cacheFile = new File(file, this.getFileMD5(url)); if (!cacheFile.exists()) {return null;} Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(cacheFile)); return bitmap != null ? bitmap : null; } catch (Exception e) { e.printStackTrace(); } return null; } /*** * 把图片存放在本地 * @param bitmap */ private void addDiskCacheBitmap(Bitmap bitmap, String url) { if (this.getDiskCacheBitmap(url) != null) {return;} try { String fileName = this.getFileMD5(url); long size = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { size = bitmap.getAllocationByteCount(); } File path = this.getDiskDir(size); if (path == null) { return; } File file = new File(path, fileName); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file)); } catch (Exception e) { e.printStackTrace(); } } /** * 判断外置sd卡是否已满, * 不满就判断文件夹是否存在,不存在就创建文件夹,存在就删除文件 * <code>{@link #deleteFile(File filePath, long fileSize)}</code>方法 * @return 存储图片文件的路径对象 */ private File getDiskDir(long fileSize) { File cacheDir = mContext.getCacheDir(); File file = mDiskPath; File path = null; if (diskIsFull(file)) { if (!file.getAbsolutePath().equals(cacheDir.getAbsolutePath())) { //外置SD卡内存不足 path = new File(file, DISK_CACHE_SUBDIR); if (path.exists()) {deleteFile(path, fileSize);} else { path = new File(cacheDir, DISK_CACHE_SUBDIR); if (diskIsFull(cacheDir)) { if (path.exists()) {deleteFile(path, fileSize);} else { //走到这里就说明外置sd卡和内置内存都满了 return null; } } } } else { //内置内存满了 path = new File(cacheDir, DISK_CACHE_SUBDIR); if (diskIsFull(cacheDir)) { if (path.exists()) {deleteFile(path, fileSize);} else { //走到这里就说明内置内存满了 return null; } } } } else { //内存足够 path = new File(file, DISK_CACHE_SUBDIR); //如果子文件夹存在时,删除文件。子文件夹不存在时,创建文件夹 if (path.exists()) { deleteFile(path, fileSize); } else {path.mkdirs();} } return path; } /** * 获取磁盘路径 * @return 当外置sd卡存在时返回外置sd卡的路径,不存在是返回内置内存的路径 */ private File getDiskPath() { //判断sd卡是否存在 boolean SDIsExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); return SDIsExist ? Environment.getExternalStorageDirectory() : mContext.getCacheDir(); } /** * 判断磁盘内存是否已满 * @param file 磁盘路径 * @return true已满,false未满 */ private boolean diskIsFull(File file) { //计算磁盘可用大小 long usableSpace = file.getUsableSpace(); if (usableSpace < mDiskSize) { Log.e(TAG, "外置SD卡内存不足,无法做外置缓存,自动缓存到内置内存"); return true; } return false; } /** * 根据修改时间删除文件,首先删除时间最久的。删除文件后重新计算大小 * @param filePath 文件夹路径 * @param fileSize 需要存放的文件大小 */ private void deleteFile(File filePath, long fileSize) { //判断文件夹大小 long cacheSize = getCacheDirSize(filePath); if ((mDiskSize - cacheSize) > fileSize) {return;} File[] files = filePath.listFiles(); File delFile = null; //获取第一个文件 for (File fil : files) { if (fil.isFile()) { delFile = fil; } } //根据时间判断时间,保存时间最长的 for (File fil : files) { if (fil.isFile()) { if (fil.lastModified() < delFile.lastModified()) {delFile = fil;} } } if (delFile != null && delFile.isFile()) { delFile.delete(); } //递归 deleteFile(filePath, fileSize); } /**递归方式 计算缓存文件的大小*/ private long getCacheDirSize(final File file) { if (file.isFile()) { return file.length(); } final File[] children = file.listFiles(); long total = 0; if (children != null) { for (final File child : children) { total += getCacheDirSize(child); } } return total; } //------------------>end 磁盘图片存储和获取<------------------ //------------------>start 公共方法,对外方法<---------------- /** * 初始化对象 * @param context 上下文 */ public static ImageCacheUtils init(Context context) { if (mUtils == null) { mUtils = new ImageCacheUtils(context); } return mUtils; } /** * 获取对象 * @return 本类对象,链式调用 */ public static ImageCacheUtils with() { if (mUtils == null) { throw new RuntimeException( "ImageCacheUtils初始化失败,请在Application的onCreate方法中调用init(Context context)方法进行初始化"); } return mUtils; } /** * 设置可用虚拟内存的缓存大小 * @param size 最少为2(当等于2,表示占可用虚拟内存的1/2,如此类推),不设置默认为4(就是1/4) * @return 本类对象,链式调用 */ public ImageCacheUtils setMemorySize(int size) { if (size < 2) { this.mLruSize = 2; return this; } this.mLruSize = size; return this; } /** * 设置磁盘可用空间的缓存大小 * @param size 单位M(默认是30M) * @return 本类对象,链式调用 */ public ImageCacheUtils setDiskSize(int size) { if (size < 0) {return this;} mDiskSize = size * 1024 * 1024; return this; } /** * 设置失败重连的次数 * @param beats 默认是3次 * @return 本类对象,链式调用 */ public ImageCacheUtils setRelinkBeats(int beats) { if (beats < 0) {return this;} this.mBeats = beats; return this; } /** * 设置是否在所有网络下加载图片,先加载磁盘或者是缓存中的,如果都没有才选择是否请求网络 * @param isOnAllNet 默认true在所有网络下都加载,false不加载(在wifi网络下加载,2/3/4G网络下不加载显示默认图片) * @return 本类对象,链式调用 */ public ImageCacheUtils setLoadImgOnAllNet(boolean isOnAllNet) { this.mIsLoadImg = isOnAllNet; return this; } /** * 设置图片的链接地址 * @param url 图片地址 * @return 本类对象,链式调用 */ public ImageCacheUtils load(String url) { mImgUrl = url; return this; } /** * 默认或网络请求失败后显示的图片 * @param resourceId 图片的资源ID * @return 本类对象,链式调用 */ public ImageCacheUtils placeholder(int resourceId) { mBitmap = BitmapFactory.decodeResource(mRes, resourceId); return this; } /** * <br>设置图片(必须是ImageView的子类或者 有setImageBitmap(Bitmap bm)这个方法) * ,调用这个方法之前请先调用<code>{@link #placeholder(int resourceId)}</code>方法, * 标记已经设置好,不会造成图片错位。(如果有调用<code>{@link #load(String url)}</code>这个方法, * 在加载前会先显示该图片) * <br>1.从缓存里面读取图片 * <br>2.从磁盘读取 * <br>3.从网络加载 * <br><b>注:该方法应当在最后调用 * @param img 显示图片的控件 */ public void into(final ImageView img) { if (mBitmap != null) { img.setImageBitmap(mBitmap); } if (TextUtils.isEmpty(mImgUrl)) { throw new NullPointerException("网络地址为空,请先调用load(String url)方法!"); } img.setTag(mImgUrl); mCacheThread.execute(new Runnable() { @Override public void run() { //子线程 new SyncNetWork(img).netWork(mImgUrl); } }); } //------------------>end 公共方法,对外方法<------------------ //------------------>start 其他方法<---------------- /**获取MD5码*/ private String getFileMD5(String info) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(info.getBytes("UTF-8")); byte[] encryption = md5.digest(); StringBuffer strBuf = new StringBuffer(); for (int i = 0; i < encryption.length; i++) { if (Integer.toHexString(0xff & encryption[i]).length() == 1) { strBuf.append("0").append(Integer.toHexString(0xff & encryption[i])); } else { strBuf.append(Integer.toHexString(0xff & encryption[i])); } } return strBuf.toString(); } catch (NoSuchAlgorithmException e) { return ""; } catch (UnsupportedEncodingException e) { return ""; } } /**是否是wifi状态true是*/ private boolean isWifi() { return getNetworkState() == NETWORN_WIFI; } /** * 获取当前网络连接类型 * @return */ private int getNetworkState() { //获取系统的网络服务 ConnectivityManager connManager = (ConnectivityManager) this.mContext.getSystemService( Context.CONNECTIVITY_SERVICE); //如果当前没有网络 if (null == connManager) { return NETWORN_NONE; } //获取当前网络类型,如果为空,返回无网络 NetworkInfo activeNetInfo = connManager.getActiveNetworkInfo(); if (activeNetInfo == null || !activeNetInfo.isAvailable()) { return NETWORN_NONE; } // 判断是不是连接的是不是wifi NetworkInfo wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (null != wifiInfo) { NetworkInfo.State state = wifiInfo.getState(); if (null != state) { if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) { return NETWORN_WIFI; } } } // 如果不是wifi,则判断当前连接的是运营商的哪种网络2g、3g、4g等 NetworkInfo networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); if (null != networkInfo) { NetworkInfo.State state = networkInfo.getState(); String strSubTypeName = networkInfo.getSubtypeName(); if (null != state) { if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) { switch (activeNetInfo.getSubtype()) { //如果是2g类型 case TelephonyManager.NETWORK_TYPE_GPRS: // 联通2g case TelephonyManager.NETWORK_TYPE_CDMA: // 电信2g case TelephonyManager.NETWORK_TYPE_EDGE: // 移动2g case TelephonyManager.NETWORK_TYPE_1xRTT: case TelephonyManager.NETWORK_TYPE_IDEN: return NETWORN_2G; //如果是3g类型 case TelephonyManager.NETWORK_TYPE_EVDO_A: // 电信3g case TelephonyManager.NETWORK_TYPE_UMTS: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_HSDPA: case TelephonyManager.NETWORK_TYPE_HSUPA: case TelephonyManager.NETWORK_TYPE_HSPA: case TelephonyManager.NETWORK_TYPE_EVDO_B: case TelephonyManager.NETWORK_TYPE_EHRPD: case TelephonyManager.NETWORK_TYPE_HSPAP: return NETWORN_3G; //如果是4g类型 case TelephonyManager.NETWORK_TYPE_LTE: return NETWORN_4G; default: //中国移动 联通 电信 三种3G制式 if (strSubTypeName.equalsIgnoreCase( "TD-SCDMA") || strSubTypeName.equalsIgnoreCase( "WCDMA") || strSubTypeName.equalsIgnoreCase("CDMA2000")) { return NETWORN_3G; } else { return NETWORN_MOBILE; } } } } } return NETWORN_NONE; } //------------------>end 其他方法<------------------ private class SyncNetWork { private int mFlag = 0;//重连标记 private ImageView mImg = null; private Bitmap mCacheBitmap = null; public SyncNetWork(ImageView img) { this.mImg = img; } /** * 连接网络加载图片 * @param imgUrl 图片的URL */ public void netWork(final String imgUrl) { //读取缓存中的图片 mCacheBitmap = getMemoryBitmap(imgUrl); if (mCacheBitmap != null) { runOnUiThread(imgUrl); return; } //读取磁盘中的图片 mCacheBitmap = getDiskCacheBitmap(imgUrl); if (mCacheBitmap != null) { runOnUiThread(imgUrl); return; } if (!mIsLoadImg) { //只在wifi下加载 if (isWifi()) { //加载 loadImg(imgUrl); } return; } loadImg(imgUrl); } /** * 加载网络图片 * @param imgUrl 图片网络地址 */ private void loadImg(final String imgUrl) { InputStream is = null; try { URL url = new URL(imgUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { mFlag = 0;//清零 is = conn.getInputStream(); if (is == null) { this.reLink(imgUrl); return; } final Bitmap bitmap = BitmapFactory.decodeStream(is); if (bitmap == null) {return;} addMemoryBitmap(bitmap, imgUrl); addDiskCacheBitmap(bitmap, imgUrl); mHandler.post(new Runnable() { @Override public void run() { //主线程 if (mImg != null && mImg.getTag().equals(imgUrl)) { mImg.setImageBitmap(bitmap); } else { if (mBitmap != null) { mImg.setImageBitmap(mBitmap); } reLink(imgUrl); } } }); } else { //需要重新连接 this.reLink(imgUrl); } if (is != null) {is.close();} } catch (Exception e) { //需要重新连接 this.reLink(imgUrl); } } /**失败重连*/ private void reLink(String imgUrl) { if (mFlag < mBeats) { SystemClock.sleep(1000); Log.d(TAG, "正在重新连接...(" + mFlag + ")" + ",当前线程" + Thread.currentThread().getName()); mFlag++; this.netWork(imgUrl); } } /** * 主线程运行 * @param imgUrl 图片的URL 用来做比较比较 */ private void runOnUiThread(String imgUrl) { if (mImg != null && imgUrl.equals(mImg.getTag())) { mHandler.post(new Runnable() { @Override public void run() { mImg.setImageBitmap(mCacheBitmap); } }); } } }}
ImageCacheUtils init = ImageCacheUtils.init(getApplicationContext()); init.setDiskSize(20);//设置磁盘大小,按需要 默认30M init.setLoadImgOnAllNet(false);//设置网络加载图片,按需要 默认true,所有网络都加载 init.setMemorySize(2);//设置缓存大小,按需要 这里1/2默认1/4 init.setRelinkBeats(3);//设置重连次数,按需要 默认是3次
AndroidManifest.xml清单文件:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>也别忘了在applocation节点加上:android:name="xxxx"这句。
调用方式:
ImageCacheUtils.with().load(图片地址).placeholder(默认的图片).into(ImageView);有点类似于Grild,是的,用了Grild的变量名
其他公开的方法上面都有注释,这里就不说了。说说这流程吧
获取对象(单例)-->设置属性-->设置URL-->设置图片
反复调用:设置URL-->设置图片------>内部类执行,调用一次创建一个内部类对象
内部类对象:
1.检查内存,内存有,用内存,如果没有↓
2.检查磁盘,磁盘有,用磁盘,如果没有↓
3.网络请求,获取图片,先存到内存,再存到磁盘
磁盘的储存流程请看简单的流程图:
好了,就说到这里吧,如有问题,请发邮件到:binary_56@foxmail.com
转载时请标明出处,谢谢!
1 0
- 安卓图片三级缓存的集装类
- 安卓的图片三级缓存
- "安卓网络请求图片三级缓存"-带您写一个自定义图片三级缓存.
- 安卓三级缓存的小知识
- 安卓三级缓存的相关知识
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级 缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 图片的三级缓存
- 终端复制命令 SSH Key 生成 git clone 出错
- ansible通过设置密码的私钥连接实现免密码登录
- spring配置文件详解
- Reverse Words in a String
- CentOS系统下使用 yum 安装 php-mysql 扩展时报错
- 安卓图片三级缓存的集装类
- 1006- Python 字典(Dictionary) items()方法
- Android文字与按钮的GrayText_Button公共类
- HDU 4607 Park Visit
- HDU 1032 The 3n + 1 problem
- Android Webview实现文件下载功能
- 11.9考试总结
- Android 优化布局层次结构
- 通过lspci如何找到驱动?