以用户管理为例,记录用户登录信息:
1.用户管理类
/** * 用户管理类 */ public class UserManager { public static final String CACHE_KEY_CURRENT_USER = "current_user"; /** * 设置当前登录用户信息(登录成功后调用) * * @param userInfo */ public static void setCurrentUser(UserEduBean userInfo) { CacheManager.getGlobalCache().putCache(CACHE_KEY_CURRENT_USER, userInfo); } /** * 获取当前登录用户信息 * * @return */ public static UserEduBean getCurrentUser() { return (UserEduBean) CacheManager.getGlobalCache().getCache(CACHE_KEY_CURRENT_USER); } /** * 清除当前登录用户信息(退出登录时候调用) */ public static void clearCurrentUser() { CacheManager.getGlobalCache().remove(CACHE_KEY_CURRENT_USER); } }2.缓存管理类:
/** * * 缓存管理类(实例化缓存对象) */ public class CacheManager { private static GlobalCache sClobalCache; public static GlobalCache getGlobalCache() { if (sClobalCache == null) { sClobalCache = new GlobalCache(); sClobalCache.init(); } return sClobalCache; } } 3.应用全局cache:/** * 应用全局cache,用于数据存储,一般情况下不清除。 * 存放内容如应用状态、当前登录状态等。 */ public class GlobalCache extends BaseCache { @Override protected String getCacheDir() { return GlobalVars.getAppFilesDir() + File.separator + "cache" + File.separator + "global"; } @Override protected String getTAG() { return "GlobalCache"; } }4.缓存操作基类:/** * 缓存操作基类 */ public abstract class BaseCache { private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb private DiskLruCache mCache; private DiskLruCache.Editor mEditor; private String CACHE_FILE_DIR; public void init() { CACHE_FILE_DIR = getCacheDir(); try { mCache = DiskLruCache.open(new File(CACHE_FILE_DIR), 1, 1, MAX_SIZE); } catch (IOException e) { e.printStackTrace(); } } public Editor putCache(String key, Object obj) { String cacheKey = MD5.encrypt(key); try { mEditor = mCache.edit(cacheKey); if (mEditor != null) { OutputStream outputStream = mEditor.newOutputStream(0); ObjectOutputStream oos = new ObjectOutputStream(outputStream); oos.writeObject(obj); mEditor.commit(); } mCache.flush(); LogUtil.info(getTAG(), "成功将" + key + "对应的内容写入缓存!"); return mEditor; } catch (IOException e) { e.printStackTrace(); return null; } } public Object getCache(String key) { Object obj = null; if (key == null) { return null; } String cacheKey = MD5.encrypt(key); DiskLruCache.Snapshot snapShot = null; InputStream is = null; ObjectInputStream ois = null; try { snapShot = mCache.get(cacheKey); if (snapShot != null) { is = snapShot.getInputStream(0); ois = new ObjectInputStream(is); obj = ois.readObject(); LogUtil.info(getTAG(), "成功将" + key + "对应的内容从缓存中读取!"); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (is != null) try { is.close(); } catch (IOException e) { } if (ois != null) try { ois.close(); } catch (IOException e) { } } return obj; } public void remove(String key) { String cacheKey = MD5.encrypt(key); try { mCache.remove(cacheKey); } catch (IOException e) { e.printStackTrace(); } } /** * 将所有的缓存数据全部删除 */ public void removeAll() { try { DiskLruCache.deleteContents(new File(CACHE_FILE_DIR)); } catch (IOException e) { e.printStackTrace(); } } /** * 将DiskLruCache关闭掉,关闭掉了之后就不能再进行任何操作 */ public void close() { try { mCache.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 获取缓存大小 */ public String getCacheSize() { LogUtil.info("CacheSize", mCache.size() + ""); if ((mCache.size() / 1024) > 0) { return (mCache.size() / 1024) + "kb"; } else { return "0kb"; } } protected abstract String getCacheDir(); protected abstract String getTAG(); } 5.核心管理类DiskLruCache:public final class DiskLruCache implements Closeable { static final String JOURNAL_FILE = "journal"; static final String JOURNAL_FILE_TMP = "journal.tmp"; static final String MAGIC = "libcore.io.DiskLruCache"; static final String VERSION_1 = "1"; static final long ANY_SEQUENCE_NUMBER = -1; private static final String CLEAN = "CLEAN"; private static final String DIRTY = "DIRTY"; private static final String REMOVE = "REMOVE"; private static final String READ = "READ"; private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final int IO_BUFFER_SIZE = 8 * 1024; /* * This cache uses a journal file named "journal". A typical journal file * looks like this: libcore.io.DiskLruCache 1 100 2 * * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 DIRTY * 335c4c6028171cfddfbaae1a9c313c52 CLEAN 335c4c6028171cfddfbaae1a9c313c52 * 3934 2342 REMOVE 335c4c6028171cfddfbaae1a9c313c52 DIRTY * 1ab96a171faeeee38496d8b330771a7a CLEAN 1ab96a171faeeee38496d8b330771a7a * 1600 234 READ 335c4c6028171cfddfbaae1a9c313c52 READ * 3400330d1dfc7f3f7f4b8d4d803dfcf6 * * The first five lines of the journal form its header. They are the * constant string "libcore.io.DiskLruCache", the disk cache's version, the * application's version, the value count, and a blank line. * * Each of the subsequent lines in the file is a record of the state of a * cache entry. Each line contains space-separated values: a state, a key, * and optional state-specific values. o DIRTY lines track that an entry is * actively being created or updated. Every successful DIRTY action should * be followed by a CLEAN or REMOVE action. DIRTY lines without a matching * CLEAN or REMOVE indicate that temporary files may need to be deleted. o * CLEAN lines track a cache entry that has been successfully published and * may be read. A publish line is followed by the lengths of each of its * values. o READ lines track accesses for LRU. o REMOVE lines track entries * that have been deleted. * * The journal file is appended to as cache operations occur. The journal * may occasionally be compacted by dropping redundant lines. A temporary * file named "journal.tmp" will be used during compaction; that file should * be deleted if it exists when the cache is opened. */ private final File directory; private final File journalFile;// 日志文件 private final File journalFileTmp;// 日志文件临时文件 private final int appVersion;// 应用Version private final long maxSize;// 最大空间 private final int valueCount;// key对应的value的个数 private long size = 0; private Writer journalWriter; private final LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<String, Entry>( 0, 0.75f, true); private int redundantOpCount; /** * To differentiate between old and current snapshots, each entry is given a * sequence number each time an edit is committed. A snapshot is stale if * its sequence number is not equal to its entry's sequence number. * 区分老的和当前的快照,每一个实体在每次编辑被committed时都被赋予一个序列号。 一个快照的序列号如果不等于entry的序列号那它就是废弃的。 */ private long nextSequenceNumber = 0; /* From java.util.Arrays 数组拷贝 */ @SuppressWarnings("unchecked") private static <T> T[] copyOfRange(T[] original, int start, int end) { final int originalLength = original.length; // For exception priority // compatibility. if (start > end) { throw new IllegalArgumentException(); } if (start < 0 || start > originalLength) { throw new ArrayIndexOutOfBoundsException(); } final int resultLength = end - start; final int copyLength = Math.min(resultLength, originalLength - start); final T[] result = (T[]) Array.newInstance(original.getClass() .getComponentType(), resultLength); System.arraycopy(original, start, result, 0, copyLength); return result; } /** * Returns the remainder of 'reader' as a string, closing it when done. * 返回String的值,然后close */ public static String readFully(Reader reader) throws IOException { try { StringWriter writer = new StringWriter(); char[] buffer = new char[1024]; int count; while ((count = reader.read(buffer)) != -1) { writer.write(buffer, 0, count); } return writer.toString(); } finally { reader.close(); } } /** * Returns the ASCII characters up to but not including the next "\r\n", or * "\n". * * @throws EOFException * if the stream is exhausted before the next newline character. * 读取输入流中返回的某行ASCII码字符 */ public static String readAsciiLine(InputStream in) throws IOException { // TODO: support UTF-8 here instead StringBuilder result = new StringBuilder(80); while (true) { int c = in.read(); if (c == -1) { throw new EOFException(); } else if (c == '\n') { break; } result.append((char) c); } int length = result.length(); if (length > 0 && result.charAt(length - 1) == '\r') { result.setLength(length - 1); } return result.toString(); } /** * Closes 'closeable', ignoring any checked exceptions. Does nothing if * 'closeable' is null. closeable关闭 */ public static void closeQuietly(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { } } } /** * Recursively delete everything in {@code dir}. 递归删除dir */ // TODO: this should specify paths as Strings rather than as Files public static void deleteContents(File dir) throws IOException { File[] files = dir.listFiles(); if (files == null) { throw new IllegalArgumentException("not a directory: " + dir); } for (File file : files) { if (file.isDirectory()) { deleteContents(file); } if (!file.delete()) { throw new IOException("failed to delete file: " + file); } } } /** * This cache uses a single background thread to evict entries. 后台单线程回收entry */ private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); private final Callable<Void> cleanupCallable = new Callable<Void>() { @Override public Void call() throws Exception { synchronized (DiskLruCache.this) { if (journalWriter == null) { return null; // closed } trimToSize(); if (journalRebuildRequired()) { rebuildJournal(); redundantOpCount = 0; } } return null; } }; // 构造器 private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) { this.directory = directory; this.appVersion = appVersion; this.journalFile = new File(directory, JOURNAL_FILE); this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP); this.valueCount = valueCount; this.maxSize = maxSize; } /** * * step1: 打开指定目录(directory)的缓存,如果还不存在就会创建。 Opens the cache in * {@code directory}, creating a cache if none exists there. * * @param directory * 数据的缓存目录 * @param appVersion * 应用程序的版本号 * @param valueCount * 同一个key可以对应多少个缓存文件,一般都是传1 * @param maxSize * 最多可以缓存多少字节的数据 * @throws IOException * if reading or writing the cache directory fails */ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } if (valueCount <= 0) { throw new IllegalArgumentException("valueCount <= 0"); } // prefer to pick up where we left off 优先处理先前的cache DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); if (cache.journalFile.exists()) { try { cache.readJournal(); cache.processJournal(); cache.journalWriter = new BufferedWriter(new FileWriter( cache.journalFile, true), IO_BUFFER_SIZE); return cache; } catch (IOException journalIsCorrupt) { // System.logW("DiskLruCache " + directory + " is corrupt: " // + journalIsCorrupt.getMessage() + ", removing"); cache.delete(); } } // create a new empty cache 创建一个空新的cache directory.mkdirs(); cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); cache.rebuildJournal(); return cache; } // 读取日志信息 private void readJournal() throws IOException { InputStream in = new BufferedInputStream(new FileInputStream( journalFile), IO_BUFFER_SIZE); try { String magic = readAsciiLine(in); String version = readAsciiLine(in); String appVersionString = readAsciiLine(in); String valueCountString = readAsciiLine(in); String blank = readAsciiLine(in); if (!MAGIC.equals(magic) || !VERSION_1.equals(version) || !Integer.toString(appVersion).equals(appVersionString) || !Integer.toString(valueCount).equals(valueCountString) || !"".equals(blank)) { throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " + valueCountString + ", " + blank + "]"); } while (true) { try { readJournalLine(readAsciiLine(in)); ;// 读取日志信息 } catch (EOFException endOfJournal) { break; } } } finally { closeQuietly(in);// 关闭输入流 } } // 读取日志中某行日志信息 private void readJournalLine(String line) throws IOException { String[] parts = line.split(" "); if (parts.length < 2) { throw new IOException("unexpected journal line: " + line); } String key = parts[1]; if (parts[0].equals(REMOVE) && parts.length == 2) { lruEntries.remove(key); return; } Entry entry = lruEntries.get(key); if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) { entry.readable = true; entry.currentEditor = null; entry.setLengths(copyOfRange(parts, 2, parts.length)); } else if (parts[0].equals(DIRTY) && parts.length == 2) { entry.currentEditor = new Editor(entry); } else if (parts[0].equals(READ) && parts.length == 2) { // this work was already done by calling lruEntries.get() } else { throw new IOException("unexpected journal line: " + line); } } /** * Computes the initial size and collects garbage as a part of opening the * cache. Dirty entries are assumed to be inconsistent and will be deleted. * 处理日志 计算初始化cache的初始化大小和收集垃圾。Dirty entry假定不一致将会被删掉。 */ private void processJournal() throws IOException { deleteIfExists(journalFileTmp);// 删除日志文件 for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext();) { Entry entry = i.next(); if (entry.currentEditor == null) { for (int t = 0; t < valueCount; t++) { size += entry.lengths[t]; } } else { entry.currentEditor = null; for (int t = 0; t < valueCount; t++) { deleteIfExists(entry.getCleanFile(t)); deleteIfExists(entry.getDirtyFile(t)); } i.remove(); } } } /** * Creates a new journal that omits redundant information. This replaces the * current journal if it exists. 创建一个新的删掉冗余信息的日志。替换当前的日志 */ private synchronized void rebuildJournal() throws IOException { if (journalWriter != null) { journalWriter.close(); } Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE); writer.write(MAGIC); writer.write("\n"); writer.write(VERSION_1); writer.write("\n"); writer.write(Integer.toString(appVersion)); writer.write("\n"); writer.write(Integer.toString(valueCount)); writer.write("\n"); writer.write("\n"); for (Entry entry : lruEntries.values()) { if (entry.currentEditor != null) { writer.write(DIRTY + ' ' + entry.key + '\n'); } else { writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); } } writer.close(); journalFileTmp.renameTo(journalFile); journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE); } // 文件若存在删除 private static void deleteIfExists(File file) throws IOException { // try { // Libcore.os.remove(file.getPath()); // } catch (ErrnoException errnoException) { // if (errnoException.errno != OsConstants.ENOENT) { // throw errnoException.rethrowAsIOException(); // } // } if (file.exists() && !file.delete()) { throw new IOException(); } } /** * step1: 根据key来获取到相应的缓存快照对象 Returns a snapshot of the entry named * {@code key}, or null if it doesn't exist is not currently readable. If a * value is returned, it is moved to the head of the LRU queue. * 返回key对应的entry的snapshot,当key相应的entry不存在或者当前不可读时返回null。 * 如果返回相应的值,它就会被移动到LRU队列的头部。 */ public synchronized Snapshot get(String key) throws IOException { checkNotClosed();// 检查cache是否已关闭 validateKey(key);// 验证key格式的正确性 Entry entry = lruEntries.get(key); if (entry == null) { return null; } if (!entry.readable) { return null; } /* * Open all streams eagerly to guarantee that we see a single published * snapshot. If we opened streams lazily then the streams could come * from different edits. */ InputStream[] ins = new InputStream[valueCount]; try { for (int i = 0; i < valueCount; i++) { ins[i] = new FileInputStream(entry.getCleanFile(i)); } } catch (FileNotFoundException e) { // a file must have been deleted manually! return null; } redundantOpCount++; journalWriter.append(READ + ' ' + key + '\n'); if (journalRebuildRequired()) { executorService.submit(cleanupCallable); } return new Snapshot(key, entry.sequenceNumber, ins); } /** * step2: 返回针对指定key的缓存编辑器 Returns an editor for the entry named {@code key}, * or null if another edit is in progress. */ public Editor edit(String key) throws IOException { return edit(key, ANY_SEQUENCE_NUMBER); } private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { checkNotClosed();// 检查cache关闭与否 validateKey(key);// 验证key格式正确性 Entry entry = lruEntries.get(key); if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) { return null; // snapshot is stale } if (entry == null) { entry = new Entry(key); lruEntries.put(key, entry); } else if (entry.currentEditor != null) { return null; // another edit is in progress } Editor editor = new Editor(entry); entry.currentEditor = editor; // flush the journal before creating files to prevent file leaks journalWriter.write(DIRTY + ' ' + key + '\n'); journalWriter.flush(); return editor; } /** * Returns the directory where this cache stores its data. */ public File getDirectory() { return directory; } /** * Returns the maximum number of bytes that this cache should use to store * its data. */ public long maxSize() { return maxSize; } /** * Returns the number of bytes currently being used to store the values in * this cache. This may be greater than the max size if a background * deletion is pending. */ public synchronized long size() { return size; } // 完成Edit动作 private synchronized void completeEdit(Editor editor, boolean success) throws IOException { Entry entry = editor.entry; if (entry.currentEditor != editor) { throw new IllegalStateException(); } // if this edit is creating the entry for the first time, every index // must have a value if (success && !entry.readable) { for (int i = 0; i < valueCount; i++) { if (!entry.getDirtyFile(i).exists()) { editor.abort(); throw new IllegalStateException("edit didn't create file " + i); } } } for (int i = 0; i < valueCount; i++) { File dirty = entry.getDirtyFile(i); if (success) { if (dirty.exists()) { File clean = entry.getCleanFile(i); dirty.renameTo(clean); long oldLength = entry.lengths[i]; long newLength = clean.length(); entry.lengths[i] = newLength; size = size - oldLength + newLength; } } else { deleteIfExists(dirty); } } redundantOpCount++; entry.currentEditor = null; if (entry.readable | success) { entry.readable = true; journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); if (success) { entry.sequenceNumber = nextSequenceNumber++; } } else { lruEntries.remove(entry.key); journalWriter.write(REMOVE + ' ' + entry.key + '\n'); } if (size > maxSize || journalRebuildRequired()) { executorService.submit(cleanupCallable); } } /** * We only rebuild the journal when it will halve the size of the journal * and eliminate at least 2000 ops. 当日志大小减半并且删掉至少2000项时重新构造日志 */ private boolean journalRebuildRequired() { final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000; return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD && redundantOpCount >= lruEntries.size(); } /** * Drops the entry for {@code key} if it exists and can be removed. Entries * actively being edited cannot be removed. 删除key相应的entry,被编辑的Entry不能被remove * * @return true if an entry was removed. */ public synchronized boolean remove(String key) throws IOException { checkNotClosed();// 检查cache是否已经关闭 validateKey(key);// 验证key格式的正确性 Entry entry = lruEntries.get(key); if (entry == null || entry.currentEditor != null) { return false; } for (int i = 0; i < valueCount; i++) { File file = entry.getCleanFile(i); if (!file.delete()) { throw new IOException("failed to delete " + file); } size -= entry.lengths[i]; entry.lengths[i] = 0; } redundantOpCount++; journalWriter.append(REMOVE + ' ' + key + '\n'); lruEntries.remove(key); if (journalRebuildRequired()) { executorService.submit(cleanupCallable); } return true; } /** * Returns true if this cache has been closed. 判断cache是否已经关闭 */ public boolean isClosed() { return journalWriter == null; } // 检查cache是否已经关闭 private void checkNotClosed() { if (journalWriter == null) { throw new IllegalStateException("cache is closed"); } } /** * Force buffered operations to the filesystem. */ public synchronized void flush() throws IOException { checkNotClosed();// 检查cache是否关闭 trimToSize();// 满足最大空间limit journalWriter.flush(); } /** * Closes this cache. Stored values will remain on the filesystem. 关闭cache。 */ public synchronized void close() throws IOException { if (journalWriter == null) { return; // already closed } for (Entry entry : new ArrayList<Entry>(lruEntries.values())) { if (entry.currentEditor != null) { entry.currentEditor.abort(); } } trimToSize(); journalWriter.close(); journalWriter = null; } // 回收删除某些entry到空间大小满足maxsize private void trimToSize() throws IOException { while (size > maxSize) { // Map.Entry<String, Entry> toEvict = lruEntries.eldest(); final Map.Entry<String, Entry> toEvict = lruEntries.entrySet() .iterator().next(); remove(toEvict.getKey()); } } /** * Closes the cache and deletes all of its stored values. This will delete * all files in the cache directory including files that weren't created by * the cache. 关闭删除cache */ public void delete() throws IOException { close(); deleteContents(directory); } // 验证key格式的正确性 private void validateKey(String key) { if (key.contains(" ") || key.contains("\n") || key.contains("\r")) { throw new IllegalArgumentException( "keys must not contain spaces or newlines: \"" + key + "\""); } } // 字符串形式读出输入流的内容 private static String inputStreamToString(InputStream in) throws IOException { return readFully(new InputStreamReader(in, UTF_8)); } /** * A snapshot of the values for an entry. entry的快照 */ public final class Snapshot implements Closeable { private final String key; private final long sequenceNumber;// 序列号(同文件名称) private final InputStream[] ins;// 两个修改的文件输入流 private Snapshot(String key, long sequenceNumber, InputStream[] ins) { this.key = key; this.sequenceNumber = sequenceNumber; this.ins = ins; } /** * * Returns an editor for this snapshot's entry, or null if either the * entry has changed since this snapshot was created or if another edit * is in progress. 返回entry快照的editor,如果entry已经更新了或者另一个edit正在处理过程中返回null。 */ public Editor edit() throws IOException { return DiskLruCache.this.edit(key, sequenceNumber); } /** * step2: 创建针对指定索引(从0开始)处的文件的输入流,用于从文件中读取缓存的数据 Returns the unbuffered * stream with the value for {@code index}. */ public InputStream getInputStream(int index) { return ins[index]; } /** * Returns the string value for {@code index}. */ public String getString(int index) throws IOException { return inputStreamToString(getInputStream(index)); } @Override public void close() { for (InputStream in : ins) { closeQuietly(in); } } } /** * Edits the values for an entry. entry编辑器 */ public final class Editor { private final Entry entry; private boolean hasErrors; private Editor(Entry entry) { this.entry = entry; } /** * * Returns an unbuffered input stream to read the last committed value, * or null if no value has been committed. * 返回一个最后提交的entry的不缓存输入流,如果没有值被提交过返回null */ public InputStream newInputStream(int index) throws IOException { synchronized (DiskLruCache.this) { if (entry.currentEditor != this) { throw new IllegalStateException(); } if (!entry.readable) { return null; } return new FileInputStream(entry.getCleanFile(index)); } } /** * Returns the last committed value as a string, or null if no value has * been committed. 返回最后提交的entry的文件内容,字符串形式 */ public String getString(int index) throws IOException { InputStream in = newInputStream(index); return in != null ? inputStreamToString(in) : null; } /** * step3: 创建指定索引(从0开始)处的输出流,用于写出要缓存的数据 Returns a new unbuffered output * stream to write the value at {@code index}. If the underlying output * stream encounters errors when writing to the filesystem, this edit * will be aborted when {@link #commit} is called. The returned output * stream does not throw IOExceptions. * * @param index * ,指定索引处的缓存文件。前面就只指定了一个key对应一个缓存文件,所以一般传入0即可 * 返回一个新的无缓冲的输出流,写文件时如果潜在的输出流存在错误,这个edit将被废弃。 */ public OutputStream newOutputStream(int index) throws IOException { synchronized (DiskLruCache.this) { if (entry.currentEditor != this) { throw new IllegalStateException(); } return new FaultHidingOutputStream(new FileOutputStream( entry.getDirtyFile(index))); } } /** * Sets the value at {@code index} to {@code value}. 设置entry的value的文件的内容 */ public void set(int index, String value) throws IOException { Writer writer = null; try { writer = new OutputStreamWriter(newOutputStream(index), UTF_8); writer.write(value); } finally { closeQuietly(writer); } } /** * Commits this edit so it is visible to readers. This releases the edit * lock so another edit may be started on the same key. * commit提交编辑的结果,释放edit锁然后其它edit可以启动 */ public void commit() throws IOException { if (hasErrors) { completeEdit(this, false); remove(entry.key); // the previous entry is stale } else { completeEdit(this, true); } } /** * Aborts this edit. This releases the edit lock so another edit may be * started on the same key. 废弃edit,释放edit锁然后其它edit可以启动 */ public void abort() throws IOException { completeEdit(this, false); } // 包装的输出流类 private class FaultHidingOutputStream extends FilterOutputStream { private FaultHidingOutputStream(OutputStream out) { super(out); } @Override public void write(int oneByte) { try { out.write(oneByte); } catch (IOException e) { hasErrors = true; } } @Override public void write(byte[] buffer, int offset, int length) { try { out.write(buffer, offset, length); } catch (IOException e) { hasErrors = true; } } @Override public void close() { try { out.close(); } catch (IOException e) { hasErrors = true; } } @Override public void flush() { try { out.flush(); } catch (IOException e) { hasErrors = true; } } } } /** * Entry 最终类 * * @author mayu * */ private final class Entry { private final String key; /** Lengths of this entry's files. */ private final long[] lengths;// 每一个cache文件的长度 /** True if this entry has ever been published */ private boolean readable; /** The ongoing edit or null if this entry is not being edited. */ private Editor currentEditor; /** * The sequence number of the most recently committed edit to this * entry. */ private long sequenceNumber; private Entry(String key) { this.key = key; this.lengths = new long[valueCount]; } public String getLengths() throws IOException { StringBuilder result = new StringBuilder(); for (long size : lengths) { result.append(' ').append(size); } return result.toString(); } /** * Set lengths using decimal numbers like "10123". * 设置每一个cache文件的长度(即lengths[i]的长度) */ private void setLengths(String[] strings) throws IOException { if (strings.length != valueCount) { throw invalidLengths(strings); } try { for (int i = 0; i < strings.length; i++) { lengths[i] = Long.parseLong(strings[i]); } } catch (NumberFormatException e) { throw invalidLengths(strings); } } private IOException invalidLengths(String[] strings) throws IOException { throw new IOException("unexpected journal line: " + Arrays.toString(strings)); } public File getCleanFile(int i) { return new File(directory, key + "." + i); } public File getDirtyFile(int i) { return new File(directory, key + "." + i + ".tmp"); } } } 6.缓存清理类:/** * 缓存清理类 */ public class DataCleanManager { public static String getDownCacheP() { return Environment.getDownloadCacheDirectory().getPath(); } public static String getDownCacheAp() { return Environment.getDownloadCacheDirectory().getAbsolutePath(); } public static String getCaDir(Context context) { return context.getCacheDir().getPath(); } public static String getExterCaDir(Context context) { return context.getExternalCacheDir().getPath(); } /** * * 清除本应用内部缓存(/data/data/com.xxx.xxx/cache) * * * * @param context */ public static void cleanInternalCache(Context context) { deleteFilesByDirectory(context.getCacheDir()); } /** * * 清除本应用所有数据库(/data/data/com.xxx.xxx/databases) * * * * @param context */ public static void cleanDatabases(Context context) { deleteFilesByDirectory(new File("/data/data/" + context.getPackageName() + "/databases")); } /** * * 清除本应用SharedPreference(/data/data/com.xxx.xxx/shared_prefs) * * * @param context */ public static void cleanSharedPreference(Context context) { deleteFilesByDirectory(new File("/data/data/" + context.getPackageName() + "/shared_prefs")); } /** * * 按名字清除本应用数据库 * * * * @param context * @param dbName */ public static void cleanDatabaseByName(Context context, String dbName) { context.deleteDatabase(dbName); } /** * * 清除/data/data/com.xxx.xxx/files下的内容 * * * * @param context */ public static void cleanFiles(Context context) { deleteFilesByDirectory(context.getFilesDir()); } /** * * 清除外部cache下的内容(/mnt/sdcard/android/data/com.xxx.xxx/cache) * * @param context */ public static void cleanExternalCache(Context context) { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { deleteFilesByDirectory(context.getExternalCacheDir()); } } /** * * 清除自定义路径下的文件,使用需小心,请不要误删。而且只支持目录下的文件删除 * * * * @param filePath */ public static void cleanCustomCache(String filePath) { deleteFilesByDirectory(new File(filePath)); } /** * * 清除本应用所有的数据 * * * * @param context * @param filepath */ public static void cleanApplicationData(Context context, String... filepath) { cleanInternalCache(context); cleanExternalCache(context); cleanDatabases(context); cleanSharedPreference(context); cleanFiles(context); if (filepath == null) { return; } for (String filePath : filepath) { cleanCustomCache(filePath); } } /** * * 删除方法 这里只会删除某个文件夹下的文件,如果传入的directory是个文件,将不做处理 * * * * @param directory */ private static void deleteFilesByDirectory(File directory) { if (directory != null && directory.exists() && directory.isDirectory()) { for (File item : directory.listFiles()) { item.delete(); } } } // 获取文件 //Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据 //Context.getExternalCacheDir() --> SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据 public static long getFolderSize(File file) throws Exception { long size = 0; try { File[] fileList = file.listFiles(); for (int i = 0; i < fileList.length; i++) { // 如果下面还有文件 if (fileList[i].isDirectory()) { size = size + getFolderSize(fileList[i]); } else { size = size + fileList[i].length(); } } } catch (Exception e) { e.printStackTrace(); } return size; } /** * 删除指定目录下文件及目录 * * @param deleteThisPath * @return */ public static void deleteFolderFile(String filePath, boolean deleteThisPath) { if (!TextUtils.isEmpty(filePath)) { try { File file = new File(filePath); if (file.isDirectory()) {// 如果下面还有文件 File files[] = file.listFiles(); for (int i = 0; i < files.length; i++) { deleteFolderFile(files[i].getAbsolutePath(), true); } } if (deleteThisPath) { if (!file.isDirectory()) {// 如果是文件,删除 file.delete(); } else {// 目录 if (file.listFiles().length == 0) {// 目录下没有文件或者目录,删除 file.delete(); } } } } catch (Exception e) { e.printStackTrace(); } } } /** * 格式化单位 * * @param size * @return */ public static String getFormatSize(double size) { double kiloByte = size / 1024; if (kiloByte < 1) { return size + "Byte"; } double megaByte = kiloByte / 1024; if (megaByte < 1) { BigDecimal result1 = new BigDecimal(Double.toString(kiloByte)); return result1.setScale(2, BigDecimal.ROUND_HALF_UP) .toPlainString() + "KB"; } double gigaByte = megaByte / 1024; if (gigaByte < 1) { BigDecimal result2 = new BigDecimal(Double.toString(megaByte)); return result2.setScale(2, BigDecimal.ROUND_HALF_UP) .toPlainString() + "MB"; } double teraBytes = gigaByte / 1024; if (teraBytes < 1) { BigDecimal result3 = new BigDecimal(Double.toString(gigaByte)); return result3.setScale(2, BigDecimal.ROUND_HALF_UP) .toPlainString() + "GB"; } BigDecimal result4 = new BigDecimal(teraBytes); return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB"; } public static String getCacheSize(File file) throws Exception { return getFormatSize(getFolderSize(file)); } }7.应用全局变量/** * 应用全局变量 */ public class GlobalVars { private static Context sContext; public static void init(Context context) { sContext = context; } /** * 获取应用私有目录 * * @return */ @SuppressLint("SdCardPath") public static String getAppFilesDir() { return "data/data/" + sContext.getPackageName(); } public static Context getContext(){ return sContext; } }