/*
* Copyright 2017 GcsSloop
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Last modified 2017-03-12 00:56:52
*
* GitHub: https://github.com/GcsSloop
* Website: http://www.gcssloop.com
* Weibo: http://weibo.com/GcsSloop
*/
package com.gcssloop.diycode.base.webview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Comparator;
public class DiskImageCache {
private static final String CACHE_SUFFIX = ".cache";
private static final int MB = 1024 * 1024;
private static final int CACHE_SIZE = 50; // 缓存占用空间大小
private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10; // 为 SD 卡保留多少空间
private File cacheDir;
public DiskImageCache(Context context) {
cacheDir = getDiskCacheDir(context, "web-image");
// 整理缓存
organizeCache(cacheDir);
}
/**
* 从缓存中获取图片
**/
public Bitmap getBitmap(final String key) {
final String path = getCachePath(key);
File file = new File(path);
if (file.exists()) {
Bitmap bmp = BitmapFactory.decodeFile(path);
if (bmp == null) {
file.delete();
} else {
updateFileTime(path);
return bmp;
}
}
return null;
}
/**
* 将图片存入文件缓存
**/
public void saveBitmap(String key, Bitmap bm) {
if (bm == null) {
return;
}
//判断sdcard上的空间
if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
return; //SD空间不足
}
File file = new File(getCachePath(key));
try {
file.createNewFile();
OutputStream outStream = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
Log.w("ImageFileCache", "FileNotFoundException");
} catch (IOException e) {
Log.w("ImageFileCache", "IOException");
}
}
/**
* 保存 bytes 数据
*
* @param key url
* @param bytes bytes 数据
*/
public void saveBytes(String key, byte[] bytes) {
if (bytes == null) {
return;
}
//判断sdcard上的空间
if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
return; //SD空间不足
}
File file = new File(getCachePath(key));
try {
file.createNewFile();
OutputStream outStream = new FileOutputStream(file);
outStream.write(bytes);
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
Log.w("ImageFileCache", "FileNotFoundException");
} catch (IOException e) {
Log.w("ImageFileCache", "IOException");
}
}
/**
* 获取一个本地缓存的输入流
*
* @param key url
* @return FileInputStream
*/
public FileInputStream getStream(String key) {
File file = new File(getCachePath(key));
if (!file.exists())
return null;
try {
FileInputStream inputStream = new FileInputStream(file);
return inputStream;
} catch (FileNotFoundException e) {
Log.e("getStream", "FileNotFoundException");
e.printStackTrace();
}
return null;
}
/**
* 获取本地缓存路径
*
* @param key url
* @return 路径
*/
public String getDiskPath(String key) {
File file = new File(getCachePath(key));
if (!file.exists())
return null;
return file.getAbsolutePath();
}
/**
* 是否有缓存
*
* @param key url
* @return 是否有缓存
*/
public boolean hasCache(String key) {
File file = new File(getCachePath(key));
return file.exists();
}
/**
* 计算存储目录下的文件大小,
* 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
* 那么删除40%最近没有被使用的文件
*/
private boolean organizeCache(@NonNull File cacheDir) {
File[] files = cacheDir.listFiles();
if (files == null) {
return true;
}
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
return false;
}
int dirSize = 0;
for (int i = 0; i < files.length; i++) {
if (files[i].getName().contains(CACHE_SUFFIX)) {
dirSize += files[i].length();
}
}
if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
int removeFactor = (int) ((0.4 * files.length) + 1);
Arrays.sort(files, new FileLastModifSort());
for (int i = 0; i < removeFactor; i++) {
if (files[i].getName().contains(CACHE_SUFFIX)) {
files[i].delete();
}
}
}
if (freeSpaceOnSd() <= CACHE_SIZE) {
return false;
}
return true;
}
/**
* 修改文件的最后修改时间
**/
public void updateFileTime(String path) {
File file = new File(path);
long newModifiedTime = System.currentTimeMillis();
file.setLastModified(newModifiedTime);
}
/**
* 计算sdcard上的剩余空间
**/
private int freeSpaceOnSd() {
StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
return (int) sdFreeMB;
}
/**
* 根据文件的最后修改时间进行排序
*/
private class FileLastModifSort implements Comparator<File> {
public int compare(File arg0, File arg1) {
if (arg0.lastModified() > arg1.lastModified()) {
return 1;
} else if (arg0.lastModified() == arg1.lastModified()) {
return 0;
} else {
return -1;
}
}
}
/**
* 获取缓存文件的绝对路径
*/
private String getCachePath(String key) {
return cacheDir.getAbsolutePath() + File.separator + convertKey(key);
}
/**
* 获取磁盘缓存文件夹 优先获取外置磁盘
*
* @param context 上下文
* @param uniqueName 自定义名字
*/
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
File cacheDir = new File(cachePath + File.separator + uniqueName);
if (!cacheDir.exists())
cacheDir.mkdir();
return cacheDir;
}
/**
* 哈希编码
*/
public String convertKey(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey + CACHE_SUFFIX;
}
private String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
}