pull/35/head
kunfei 5 years ago
parent ac537b312c
commit c0ee91f8f4
  1. 843
      app/src/main/java/io/legado/app/utils/ACache.java
  2. 810
      app/src/main/java/io/legado/app/utils/ACache.kt

@ -1,843 +0,0 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package io.legado.app.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import androidx.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* 本地缓存
*/
@SuppressWarnings({"unused", "ResultOfMethodCallIgnored", "WeakerAccess"})
public class ACache {
public static final int TIME_HOUR = 60 * 60;
public static final int TIME_DAY = TIME_HOUR * 24;
private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb
private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量
private static Map<String, ACache> mInstanceMap = new HashMap<>();
private ACacheManager mCache;
private ACache(File cacheDir, long max_size, int max_count) {
try {
if (!cacheDir.exists() && !cacheDir.mkdirs()) {
Log.i("ACache", "can't make dirs in %s" + cacheDir.getAbsolutePath());
}
mCache = new ACacheManager(cacheDir, max_size, max_count);
} catch (Exception ignored) {
}
}
public static ACache get(Context ctx) {
return get(ctx, "ACache");
}
public static ACache get(Context ctx, String cacheName) {
File f = new File(ctx.getCacheDir(), cacheName);
return get(f, MAX_SIZE, MAX_COUNT);
}
public static ACache get(File cacheDir) {
return get(cacheDir, MAX_SIZE, MAX_COUNT);
}
public static ACache get(Context ctx, long max_zise, int max_count) {
try {
File f = new File(ctx.getCacheDir(), "ACache");
return get(f, max_zise, max_count);
} catch (Exception ignored) {
}
return null;
}
public static ACache get(File cacheDir, long max_zise, int max_count) {
try {
ACache manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid());
if (manager == null) {
manager = new ACache(cacheDir, max_zise, max_count);
mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(), manager);
}
return manager;
} catch (Exception ignored) {
}
return null;
}
private static String myPid() {
return "_" + android.os.Process.myPid();
}
// =======================================
// ============ String数据 读写 ==============
// =======================================
/**
* 保存 String数据 缓存中
*
* @param key 保存的key
* @param value 保存的String数据
*/
public void put(String key, String value) {
try {
File file = mCache.newFile(key);
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(file), 1024);
out.write(value);
} catch (IOException ignored) {
} finally {
if (out != null) {
try {
out.flush();
out.close();
} catch (IOException ignored) {
}
}
mCache.put(file);
}
} catch (Exception ignored) {
}
}
/**
* 保存 String数据 缓存中
*
* @param key 保存的key
* @param value 保存的String数据
* @param saveTime 保存的时间单位
*/
public void put(String key, String value, int saveTime) {
put(key, Utils.newStringWithDateInfo(saveTime, value));
}
/**
* 读取 String数据
*
* @return String 数据
*/
@Nullable
public String getAsString(String key) {
File file = mCache.get(key);
if (!file.exists())
return null;
boolean removeFile = false;
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
StringBuilder readString = new StringBuilder();
String currentLine;
while ((currentLine = in.readLine()) != null) {
readString.append(currentLine);
}
if (!Utils.isDue(readString.toString())) {
return Utils.clearDateInfo(readString.toString());
} else {
removeFile = true;
return null;
}
} catch (IOException e) {
return null;
} finally {
if (removeFile)
remove(key);
}
}
// =======================================
// ========== JSONObject 数据 读写 =========
// =======================================
/**
* 保存 JSONObject数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSON数据
*/
public void put(String key, JSONObject value) {
put(key, value.toString());
}
/**
* 保存 JSONObject数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSONObject数据
* @param saveTime 保存的时间单位
*/
public void put(String key, JSONObject value, int saveTime) {
put(key, value.toString(), saveTime);
}
/**
* 读取JSONObject数据
*
* @return JSONObject数据
*/
public JSONObject getAsJSONObject(String key) {
String JSONString = getAsString(key);
try {
return new JSONObject(JSONString);
} catch (Exception e) {
return null;
}
}
// =======================================
// ============ JSONArray 数据 读写 =============
// =======================================
/**
* 保存 JSONArray数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSONArray数据
*/
public void put(String key, JSONArray value) {
put(key, value.toString());
}
/**
* 保存 JSONArray数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSONArray数据
* @param saveTime 保存的时间单位
*/
public void put(String key, JSONArray value, int saveTime) {
put(key, value.toString(), saveTime);
}
/**
* 读取JSONArray数据
*
* @return JSONArray数据
*/
public JSONArray getAsJSONArray(String key) {
String JSONString = getAsString(key);
try {
return new JSONArray(JSONString);
} catch (Exception e) {
return null;
}
}
// =======================================
// ============== byte 数据 读写 =============
// =======================================
/**
* 保存 byte数据 缓存中
*
* @param key 保存的key
* @param value 保存的数据
*/
public void put(String key, byte[] value) {
File file = mCache.newFile(key);
FileOutputStream out = null;
try {
out = new FileOutputStream(file);
out.write(value);
} catch (Exception ignored) {
} finally {
if (out != null) {
try {
out.flush();
out.close();
} catch (IOException ignored) {
}
}
mCache.put(file);
}
}
/**
* 保存 byte数据 缓存中
*
* @param key 保存的key
* @param value 保存的数据
* @param saveTime 保存的时间单位
*/
public void put(String key, byte[] value, int saveTime) {
put(key, Utils.newByteArrayWithDateInfo(saveTime, value));
}
/**
* 获取 byte 数据
*
* @return byte 数据
*/
public byte[] getAsBinary(String key) {
RandomAccessFile RAFile = null;
boolean removeFile = false;
try {
File file = mCache.get(key);
if (!file.exists())
return null;
RAFile = new RandomAccessFile(file, "r");
byte[] byteArray = new byte[(int) RAFile.length()];
RAFile.read(byteArray);
if (!Utils.isDue(byteArray)) {
return Utils.clearDateInfo(byteArray);
} else {
removeFile = true;
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (RAFile != null) {
try {
RAFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (removeFile)
remove(key);
}
}
// =======================================
// ============= 序列化 数据 读写 ===============
// =======================================
/**
* 保存 Serializable数据 缓存中
*
* @param key 保存的key
* @param value 保存的value
*/
public void put(String key, Serializable value) {
put(key, value, -1);
}
/**
* 保存 Serializable数据到 缓存中
*
* @param key 保存的key
* @param value 保存的value
* @param saveTime 保存的时间单位
*/
public void put(String key, Serializable value, int saveTime) {
ByteArrayOutputStream baos;
baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(value);
byte[] data = baos.toByteArray();
if (saveTime != -1) {
put(key, data, saveTime);
} else {
put(key, data);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 读取 Serializable数据
*
* @return Serializable 数据
*/
public Object getAsObject(String key) {
byte[] data = getAsBinary(key);
if (data != null) {
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
bais = new ByteArrayInputStream(data);
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
if (bais != null)
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ois != null)
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
// =======================================
// ============== bitmap 数据 读写 =============
// =======================================
/**
* 保存 bitmap 缓存中
*
* @param key 保存的key
* @param value 保存的bitmap数据
*/
public void put(String key, Bitmap value) {
put(key, Utils.Bitmap2Bytes(value));
}
/**
* 保存 bitmap 缓存中
*
* @param key 保存的key
* @param value 保存的 bitmap 数据
* @param saveTime 保存的时间单位
*/
public void put(String key, Bitmap value, int saveTime) {
put(key, Utils.Bitmap2Bytes(value), saveTime);
}
/**
* 读取 bitmap 数据
*
* @return bitmap 数据
*/
public Bitmap getAsBitmap(String key) {
if (getAsBinary(key) == null) {
return null;
}
return Utils.Bytes2Bimap(getAsBinary(key));
}
// =======================================
// ============= drawable 数据 读写 =============
// =======================================
/**
* 保存 drawable 缓存中
*
* @param key 保存的key
* @param value 保存的drawable数据
*/
public void put(String key, Drawable value) {
put(key, Utils.drawable2Bitmap(value));
}
/**
* 保存 drawable 缓存中
*
* @param key 保存的key
* @param value 保存的 drawable 数据
* @param saveTime 保存的时间单位
*/
public void put(String key, Drawable value, int saveTime) {
put(key, Utils.drawable2Bitmap(value), saveTime);
}
/**
* 读取 Drawable 数据
*
* @return Drawable 数据
*/
public Drawable getAsDrawable(String key) {
if (getAsBinary(key) == null) {
return null;
}
return Utils.bitmap2Drawable(Utils.Bytes2Bimap(getAsBinary(key)));
}
/**
* 获取缓存文件
*
* @return value 缓存的文件
*/
public File file(String key) {
try {
File f = mCache.newFile(key);
if (f.exists()) {
return f;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 移除某个key
*
* @return 是否移除成功
*/
public boolean remove(String key) {
return mCache.remove(key);
}
/**
* 清除所有数据
*/
public void clear() {
mCache.clear();
}
/**
* @author 杨福海michael www.yangfuhai.com
* @version 1.0
* title 时间计算工具类
*/
private static class Utils {
private static final char mSeparator = ' ';
/**
* 判断缓存的String数据是否到期
*
* @return true到期了 false还没有到期
*/
private static boolean isDue(String str) {
return isDue(str.getBytes());
}
/**
* 判断缓存的byte数据是否到期
*
* @return true到期了 false还没有到期
*/
private static boolean isDue(byte[] data) {
try {
String[] strs = getDateInfoFromDate(data);
if (strs != null && strs.length == 2) {
String saveTimeStr = strs[0];
while (saveTimeStr.startsWith("0")) {
saveTimeStr = saveTimeStr
.substring(1);
}
long saveTime = Long.valueOf(saveTimeStr);
long deleteAfter = Long.valueOf(strs[1]);
if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private static String newStringWithDateInfo(int second, String strInfo) {
return createDateInfo(second) + strInfo;
}
private static byte[] newByteArrayWithDateInfo(int second, byte[] data2) {
byte[] data1 = createDateInfo(second).getBytes();
byte[] retdata = new byte[data1.length + data2.length];
System.arraycopy(data1, 0, retdata, 0, data1.length);
System.arraycopy(data2, 0, retdata, data1.length, data2.length);
return retdata;
}
private static String clearDateInfo(String strInfo) {
if (strInfo != null && hasDateInfo(strInfo.getBytes())) {
strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1);
}
return strInfo;
}
private static byte[] clearDateInfo(byte[] data) {
if (hasDateInfo(data)) {
return copyOfRange(data, indexOf(data, mSeparator) + 1,
data.length);
}
return data;
}
private static boolean hasDateInfo(byte[] data) {
return data != null && data.length > 15 && data[13] == '-'
&& indexOf(data, mSeparator) > 14;
}
private static String[] getDateInfoFromDate(byte[] data) {
if (hasDateInfo(data)) {
String saveDate = new String(copyOfRange(data, 0, 13));
String deleteAfter = new String(copyOfRange(data, 14,
indexOf(data, mSeparator)));
return new String[]{saveDate, deleteAfter};
}
return null;
}
private static int indexOf(byte[] data, char c) {
for (int i = 0; i < data.length; i++) {
if (data[i] == c) {
return i;
}
}
return -1;
}
private static byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
byte[] copy = new byte[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
private static String createDateInfo(int second) {
StringBuilder currentTime = new StringBuilder(System.currentTimeMillis() + "");
while (currentTime.length() < 13) {
currentTime.insert(0, "0");
}
return currentTime + "-" + second + mSeparator;
}
/*
* Bitmap byte[]
*/
private static byte[] Bitmap2Bytes(Bitmap bm) {
if (bm == null) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
/*
* byte[] Bitmap
*/
private static Bitmap Bytes2Bimap(byte[] b) {
if (b.length == 0) {
return null;
}
return BitmapFactory.decodeByteArray(b, 0, b.length);
}
/*
* Drawable Bitmap
*/
private static Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable == null) {
return null;
}
// 取 drawable 的长宽
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// 取 drawable 的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
// 建立对应 bitmap
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
// 建立对应 bitmap 的画布
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
// 把 drawable 内容画到画布中
drawable.draw(canvas);
return bitmap;
}
/*
* Bitmap Drawable
*/
@SuppressWarnings("deprecation")
private static Drawable bitmap2Drawable(Bitmap bm) {
if (bm == null) {
return null;
}
return new BitmapDrawable(bm);
}
}
/**
* @author 杨福海michael www.yangfuhai.com
* @version 1.0
* title 缓存管理器
*/
public class ACacheManager {
private final AtomicLong cacheSize;
private final AtomicInteger cacheCount;
private final long sizeLimit;
private final int countLimit;
private final Map<File, Long> lastUsageDates = Collections
.synchronizedMap(new HashMap<>());
protected File cacheDir;
private ACacheManager(File cacheDir, long sizeLimit, int countLimit) {
this.cacheDir = cacheDir;
this.sizeLimit = sizeLimit;
this.countLimit = countLimit;
cacheSize = new AtomicLong();
cacheCount = new AtomicInteger();
calculateCacheSizeAndCacheCount();
}
/**
* 计算 cacheSize和cacheCount
*/
private void calculateCacheSizeAndCacheCount() {
new Thread(() -> {
try {
int size = 0;
int count = 0;
File[] cachedFiles = cacheDir.listFiles();
if (cachedFiles != null) {
for (File cachedFile : cachedFiles) {
size += calculateSize(cachedFile);
count += 1;
lastUsageDates.put(cachedFile,
cachedFile.lastModified());
}
cacheSize.set(size);
cacheCount.set(count);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private void put(File file) {
try {
int curCacheCount = cacheCount.get();
while (curCacheCount + 1 > countLimit) {
long freedSize = removeNext();
cacheSize.addAndGet(-freedSize);
curCacheCount = cacheCount.addAndGet(-1);
}
cacheCount.addAndGet(1);
long valueSize = calculateSize(file);
long curCacheSize = cacheSize.get();
while (curCacheSize + valueSize > sizeLimit) {
long freedSize = removeNext();
curCacheSize = cacheSize.addAndGet(-freedSize);
}
cacheSize.addAndGet(valueSize);
long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
} catch (Exception e) {
e.printStackTrace();
}
}
private File get(String key) {
File file = newFile(key);
long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
return file;
}
private File newFile(String key) {
return new File(cacheDir, key.hashCode() + "");
}
private boolean remove(String key) {
File image = get(key);
return image.delete();
}
private void clear() {
try {
lastUsageDates.clear();
cacheSize.set(0);
File[] files = cacheDir.listFiles();
if (files != null) {
for (File f : files) {
f.delete();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 移除旧的文件
*/
private long removeNext() {
try {
if (lastUsageDates.isEmpty()) {
return 0;
}
Long oldestUsage = null;
File mostLongUsedFile = null;
Set<Entry<File, Long>> entries = lastUsageDates.entrySet();
synchronized (lastUsageDates) {
for (Entry<File, Long> entry : entries) {
if (mostLongUsedFile == null) {
mostLongUsedFile = entry.getKey();
oldestUsage = entry.getValue();
} else {
Long lastValueUsage = entry.getValue();
if (lastValueUsage < oldestUsage) {
oldestUsage = lastValueUsage;
mostLongUsedFile = entry.getKey();
}
}
}
}
long fileSize = 0;
if (mostLongUsedFile != null) {
fileSize = calculateSize(mostLongUsedFile);
if (mostLongUsedFile.delete()) {
lastUsageDates.remove(mostLongUsedFile);
}
}
return fileSize;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
private long calculateSize(File file) {
return file.length();
}
}
}

@ -0,0 +1,810 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package io.legado.app.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.PixelFormat
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.util.Log
import org.json.JSONArray
import org.json.JSONObject
import java.io.*
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
/**
* 本地缓存
*/
class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) {
private lateinit var mCache: ACacheManager
init {
try {
if (!cacheDir.exists() && !cacheDir.mkdirs()) {
Log.i("ACache", "can't make dirs in %s" + cacheDir.absolutePath)
}
mCache = ACacheManager(cacheDir, max_size, max_count)
} catch (ignored: Exception) {
}
}
// =======================================
// ============ String数据 读写 ==============
// =======================================
/**
* 保存 String数据 缓存中
*
* @param key 保存的key
* @param value 保存的String数据
*/
fun put(key: String, value: String) {
try {
val file = mCache.newFile(key)
var out: BufferedWriter? = null
try {
out = BufferedWriter(FileWriter(file), 1024)
out.write(value)
} catch (ignored: IOException) {
} finally {
if (out != null) {
try {
out.flush()
out.close()
} catch (ignored: IOException) {
}
}
mCache.put(file)
}
} catch (ignored: Exception) {
}
}
/**
* 保存 String数据 缓存中
*
* @param key 保存的key
* @param value 保存的String数据
* @param saveTime 保存的时间单位
*/
fun put(key: String, value: String, saveTime: Int) {
put(key, Utils.newStringWithDateInfo(saveTime, value))
}
/**
* 读取 String数据
*
* @return String 数据
*/
fun getAsString(key: String): String? {
val file = mCache[key]
if (!file.exists())
return null
var removeFile = false
return try {
val text = file.readText()
if (!Utils.isDue(text)) {
Utils.clearDateInfo(text)
} else {
removeFile = true
null
}
} catch (e: IOException) {
null
} finally {
if (removeFile)
remove(key)
}
}
// =======================================
// ========== JSONObject 数据 读写 =========
// =======================================
/**
* 保存 JSONObject数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSON数据
*/
fun put(key: String, value: JSONObject) {
put(key, value.toString())
}
/**
* 保存 JSONObject数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSONObject数据
* @param saveTime 保存的时间单位
*/
fun put(key: String, value: JSONObject, saveTime: Int) {
put(key, value.toString(), saveTime)
}
/**
* 读取JSONObject数据
*
* @return JSONObject数据
*/
fun getAsJSONObject(key: String): JSONObject? {
val json = getAsString(key)
return try {
JSONObject(json)
} catch (e: Exception) {
null
}
}
// =======================================
// ============ JSONArray 数据 读写 =============
// =======================================
/**
* 保存 JSONArray数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSONArray数据
*/
fun put(key: String, value: JSONArray) {
put(key, value.toString())
}
/**
* 保存 JSONArray数据 缓存中
*
* @param key 保存的key
* @param value 保存的JSONArray数据
* @param saveTime 保存的时间单位
*/
fun put(key: String, value: JSONArray, saveTime: Int) {
put(key, value.toString(), saveTime)
}
/**
* 读取JSONArray数据
*
* @return JSONArray数据
*/
fun getAsJSONArray(key: String): JSONArray? {
val json = getAsString(key)
return try {
JSONArray(json)
} catch (e: Exception) {
null
}
}
// =======================================
// ============== byte 数据 读写 =============
// =======================================
/**
* 保存 byte数据 缓存中
*
* @param key 保存的key
* @param value 保存的数据
*/
fun put(key: String, value: ByteArray) {
val file = mCache.newFile(key)
var out: FileOutputStream? = null
try {
out = FileOutputStream(file)
out.write(value)
} catch (ignored: Exception) {
} finally {
if (out != null) {
try {
out.flush()
out.close()
} catch (ignored: IOException) {
}
}
mCache.put(file)
}
}
/**
* 保存 byte数据 缓存中
*
* @param key 保存的key
* @param value 保存的数据
* @param saveTime 保存的时间单位
*/
fun put(key: String, value: ByteArray, saveTime: Int) {
put(key, Utils.newByteArrayWithDateInfo(saveTime, value))
}
/**
* 获取 byte 数据
*
* @return byte 数据
*/
fun getAsBinary(key: String): ByteArray? {
var raf: RandomAccessFile? = null
var removeFile = false
try {
val file = mCache[key]
if (!file.exists())
return null
raf = RandomAccessFile(file, "r")
val byteArray = ByteArray(raf.length().toInt())
raf.read(byteArray)
return if (!Utils.isDue(byteArray)) {
Utils.clearDateInfo(byteArray)
} else {
removeFile = true
null
}
} catch (e: Exception) {
e.printStackTrace()
return null
} finally {
if (raf != null) {
try {
raf.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (removeFile)
remove(key)
}
}
/**
* 保存 Serializable数据到 缓存中
*
* @param key 保存的key
* @param value 保存的value
* @param saveTime 保存的时间单位
*/
@JvmOverloads
fun put(key: String, value: Serializable, saveTime: Int = -1) {
try {
val baos = ByteArrayOutputStream()
ObjectOutputStream(baos).use { oos ->
oos.writeObject(value)
val data = baos.toByteArray()
if (saveTime != -1) {
put(key, data, saveTime)
} else {
put(key, data)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 读取 Serializable数据
*
* @return Serializable 数据
*/
fun getAsObject(key: String): Any? {
val data = getAsBinary(key)
if (data != null) {
var bais: ByteArrayInputStream? = null
var ois: ObjectInputStream? = null
try {
bais = ByteArrayInputStream(data)
ois = ObjectInputStream(bais)
return ois.readObject()
} catch (e: Exception) {
e.printStackTrace()
return null
} finally {
try {
bais?.close()
} catch (e: IOException) {
e.printStackTrace()
}
try {
ois?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
return null
}
// =======================================
// ============== bitmap 数据 读写 =============
// =======================================
/**
* 保存 bitmap 缓存中
*
* @param key 保存的key
* @param value 保存的bitmap数据
*/
fun put(key: String, value: Bitmap) {
put(key, Utils.Bitmap2Bytes(value))
}
/**
* 保存 bitmap 缓存中
*
* @param key 保存的key
* @param value 保存的 bitmap 数据
* @param saveTime 保存的时间单位
*/
fun put(key: String, value: Bitmap, saveTime: Int) {
put(key, Utils.Bitmap2Bytes(value), saveTime)
}
/**
* 读取 bitmap 数据
*
* @return bitmap 数据
*/
fun getAsBitmap(key: String): Bitmap? {
return if (getAsBinary(key) == null) {
null
} else Utils.Bytes2Bimap(getAsBinary(key)!!)
}
// =======================================
// ============= drawable 数据 读写 =============
// =======================================
/**
* 保存 drawable 缓存中
*
* @param key 保存的key
* @param value 保存的drawable数据
*/
fun put(key: String, value: Drawable) {
put(key, Utils.drawable2Bitmap(value))
}
/**
* 保存 drawable 缓存中
*
* @param key 保存的key
* @param value 保存的 drawable 数据
* @param saveTime 保存的时间单位
*/
fun put(key: String, value: Drawable, saveTime: Int) {
put(key, Utils.drawable2Bitmap(value), saveTime)
}
/**
* 读取 Drawable 数据
*
* @return Drawable 数据
*/
fun getAsDrawable(key: String): Drawable? {
return if (getAsBinary(key) == null) {
null
} else Utils.bitmap2Drawable(
Utils.Bytes2Bimap(
getAsBinary(key)!!
)
)
}
/**
* 获取缓存文件
*
* @return value 缓存的文件
*/
fun file(key: String): File? {
try {
val f = mCache.newFile(key)
if (f.exists()) {
return f
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
/**
* 移除某个key
*
* @return 是否移除成功
*/
fun remove(key: String): Boolean {
return mCache.remove(key)
}
/**
* 清除所有数据
*/
fun clear() {
mCache.clear()
}
/**
* @author 杨福海michael www.yangfuhai.com
* @version 1.0
* title 时间计算工具类
*/
private object Utils {
private val mSeparator = ' '
/**
* 判断缓存的String数据是否到期
*
* @return true到期了 false还没有到期
*/
fun isDue(str: String): Boolean {
return isDue(str.toByteArray())
}
/**
* 判断缓存的byte数据是否到期
*
* @return true到期了 false还没有到期
*/
fun isDue(data: ByteArray): Boolean {
try {
val strs = getDateInfoFromDate(data)
if (strs != null && strs.size == 2) {
var saveTimeStr = strs[0]
while (saveTimeStr.startsWith("0")) {
saveTimeStr = saveTimeStr
.substring(1)
}
val saveTime = java.lang.Long.valueOf(saveTimeStr)
val deleteAfter = java.lang.Long.valueOf(strs[1])
if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) {
return true
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun newStringWithDateInfo(second: Int, strInfo: String): String {
return createDateInfo(second) + strInfo
}
fun newByteArrayWithDateInfo(second: Int, data2: ByteArray): ByteArray {
val data1 = createDateInfo(second).toByteArray()
val retdata = ByteArray(data1.size + data2.size)
System.arraycopy(data1, 0, retdata, 0, data1.size)
System.arraycopy(data2, 0, retdata, data1.size, data2.size)
return retdata
}
fun clearDateInfo(strInfo: String?): String? {
var strInfo = strInfo
if (strInfo != null && hasDateInfo(strInfo.toByteArray())) {
strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1)
}
return strInfo
}
fun clearDateInfo(data: ByteArray): ByteArray {
return if (hasDateInfo(data)) {
copyOfRange(
data, indexOf(data, mSeparator) + 1,
data.size
)
} else data
}
fun hasDateInfo(data: ByteArray?): Boolean {
return (data != null && data.size > 15 && data[13] == '-'.toByte()
&& indexOf(data, mSeparator) > 14)
}
fun getDateInfoFromDate(data: ByteArray): Array<String>? {
if (hasDateInfo(data)) {
val saveDate = String(copyOfRange(data, 0, 13))
val deleteAfter = String(
copyOfRange(
data, 14,
indexOf(data, mSeparator)
)
)
return arrayOf(saveDate, deleteAfter)
}
return null
}
private fun indexOf(data: ByteArray, c: Char): Int {
for (i in data.indices) {
if (data[i] == c.toByte()) {
return i
}
}
return -1
}
private fun copyOfRange(original: ByteArray, from: Int, to: Int): ByteArray {
val newLength = to - from
require(newLength >= 0) { "$from > $to" }
val copy = ByteArray(newLength)
System.arraycopy(
original, from, copy, 0,
Math.min(original.size - from, newLength)
)
return copy
}
private fun createDateInfo(second: Int): String {
val currentTime = StringBuilder(System.currentTimeMillis().toString() + "")
while (currentTime.length < 13) {
currentTime.insert(0, "0")
}
return "$currentTime-$second$mSeparator"
}
/*
* Bitmap byte[]
*/
fun Bitmap2Bytes(bm: Bitmap): ByteArray {
val baos = ByteArrayOutputStream()
bm.compress(Bitmap.CompressFormat.PNG, 100, baos)
return baos.toByteArray()
}
/*
* byte[] Bitmap
*/
fun Bytes2Bimap(b: ByteArray): Bitmap? {
return if (b.size == 0) {
null
} else BitmapFactory.decodeByteArray(b, 0, b.size)
}
/*
* Drawable Bitmap
*/
fun drawable2Bitmap(drawable: Drawable): Bitmap {
// 取 drawable 的长宽
val w = drawable.intrinsicWidth
val h = drawable.intrinsicHeight
// 取 drawable 的颜色格式
val config = if (drawable.opacity != PixelFormat.OPAQUE)
Bitmap.Config.ARGB_8888
else
Bitmap.Config.RGB_565
// 建立对应 bitmap
val bitmap = Bitmap.createBitmap(w, h, config)
// 建立对应 bitmap 的画布
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, w, h)
// 把 drawable 内容画到画布中
drawable.draw(canvas)
return bitmap
}
/*
* Bitmap Drawable
*/
fun bitmap2Drawable(bm: Bitmap?): Drawable? {
return if (bm == null) {
null
} else BitmapDrawable(bm)
}
}
/**
* @author 杨福海michael www.yangfuhai.com
* @version 1.0
* title 缓存管理器
*/
open inner class ACacheManager(
private var cacheDir: File,
private val sizeLimit: Long,
private val countLimit: Int
) {
private val cacheSize: AtomicLong = AtomicLong()
private val cacheCount: AtomicInteger = AtomicInteger()
private val lastUsageDates = Collections
.synchronizedMap(HashMap<File, Long>())
init {
calculateCacheSizeAndCacheCount()
}
/**
* 计算 cacheSize和cacheCount
*/
private fun calculateCacheSizeAndCacheCount() {
Thread {
try {
var size = 0
var count = 0
val cachedFiles = cacheDir.listFiles()
if (cachedFiles != null) {
for (cachedFile in cachedFiles) {
size += calculateSize(cachedFile).toInt()
count += 1
lastUsageDates[cachedFile] = cachedFile.lastModified()
}
cacheSize.set(size.toLong())
cacheCount.set(count)
}
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
fun put(file: File) {
try {
var curCacheCount = cacheCount.get()
while (curCacheCount + 1 > countLimit) {
val freedSize = removeNext()
cacheSize.addAndGet(-freedSize)
curCacheCount = cacheCount.addAndGet(-1)
}
cacheCount.addAndGet(1)
val valueSize = calculateSize(file)
var curCacheSize = cacheSize.get()
while (curCacheSize + valueSize > sizeLimit) {
val freedSize = removeNext()
curCacheSize = cacheSize.addAndGet(-freedSize)
}
cacheSize.addAndGet(valueSize)
val currentTime = System.currentTimeMillis()
file.setLastModified(currentTime)
lastUsageDates[file] = currentTime
} catch (e: Exception) {
e.printStackTrace()
}
}
operator fun get(key: String): File {
val file = newFile(key)
val currentTime = System.currentTimeMillis()
file.setLastModified(currentTime)
lastUsageDates[file] = currentTime
return file
}
fun newFile(key: String): File {
return File(cacheDir, key.hashCode().toString() + "")
}
fun remove(key: String): Boolean {
val image = get(key)
return image.delete()
}
fun clear() {
try {
lastUsageDates.clear()
cacheSize.set(0)
val files = cacheDir.listFiles()
if (files != null) {
for (f in files) {
f.delete()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 移除旧的文件
*/
private fun removeNext(): Long {
try {
if (lastUsageDates.isEmpty()) {
return 0
}
var oldestUsage: Long? = null
var mostLongUsedFile: File? = null
val entries = lastUsageDates.entries
synchronized(lastUsageDates) {
for ((key, lastValueUsage) in entries) {
if (mostLongUsedFile == null) {
mostLongUsedFile = key
oldestUsage = lastValueUsage
} else {
if (lastValueUsage < oldestUsage!!) {
oldestUsage = lastValueUsage
mostLongUsedFile = key
}
}
}
}
var fileSize: Long = 0
if (mostLongUsedFile != null) {
fileSize = calculateSize(mostLongUsedFile!!)
if (mostLongUsedFile!!.delete()) {
lastUsageDates.remove(mostLongUsedFile)
}
}
return fileSize
} catch (e: Exception) {
e.printStackTrace()
return 0
}
}
private fun calculateSize(file: File): Long {
return file.length()
}
}
companion object {
const val TIME_HOUR = 60 * 60
const val TIME_DAY = TIME_HOUR * 24
private const val MAX_SIZE = 1000 * 1000 * 50 // 50 mb
private const val MAX_COUNT = Integer.MAX_VALUE // 不限制存放数据的数量
private val mInstanceMap = HashMap<String, ACache>()
@JvmOverloads
operator fun get(ctx: Context, cacheName: String = "ACache"): ACache? {
val f = File(ctx.cacheDir, cacheName)
return get(f, MAX_SIZE.toLong(), MAX_COUNT)
}
operator fun get(ctx: Context, max_zise: Long, max_count: Int): ACache? {
try {
val f = File(ctx.cacheDir, "ACache")
return get(f, max_zise, max_count)
} catch (ignored: Exception) {
}
return null
}
@JvmOverloads
operator fun get(
cacheDir: File,
max_zise: Long = MAX_SIZE.toLong(),
max_count: Int = MAX_COUNT
): ACache? {
try {
var manager = mInstanceMap[cacheDir.absoluteFile.toString() + myPid()]
if (manager == null) {
manager = ACache(cacheDir, max_zise, max_count)
mInstanceMap[cacheDir.absolutePath + myPid()] = manager
}
return manager
} catch (ignored: Exception) {
}
return null
}
private fun myPid(): String {
return "_" + android.os.Process.myPid()
}
}
}
Loading…
Cancel
Save