All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mobi.cangol.mobile.service.cache.CacheManagerImpl Maven / Gradle / Ivy

There is a newer version: 1.2.7
Show newest version
/**
 * Copyright (c) 2013 Cangol
 * 

* 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. */ package mobi.cangol.mobile.service.cache; import android.annotation.TargetApi; import android.app.Application; import android.content.Context; import android.os.AsyncTask; import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.text.TextUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Iterator; import mobi.cangol.mobile.logging.Log; import mobi.cangol.mobile.service.Service; import mobi.cangol.mobile.service.ServiceProperty; import mobi.cangol.mobile.utils.Object2FileUtils; /** * @author Cangol */ @Service("CacheManager") class CacheManagerImpl implements CacheManager { private static final String TAG = "CacheManager"; private static final int DISK_CACHE_INDEX = 0; private static final long DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 20; // 20MB private final Object mDiskCacheLock = new Object(); private boolean mDebug; private DiskLruCache mDiskLruCache; private HashMap> mContextMaps = new HashMap<>(); private boolean mDiskCacheStarting = true; private File mDiskCacheDir; private long mDiskCacheSize; private ServiceProperty mServiceProperty; private Application mContext; @Override public void onCreate(Application context) { this.mContext = context; } @Override public void init(ServiceProperty serviceProperty) { this.mServiceProperty = serviceProperty; String dir = mServiceProperty.getString(CacheManager.CACHE_DIR); long size = mServiceProperty.getLong(CacheManager.CACHE_SIZE); setDiskCache( !TextUtils.isEmpty(dir) ? getDiskCacheDir(mContext, dir) : getDiskCacheDir(mContext, "ContentCache"), size > 0 ? size : DEFAULT_DISK_CACHE_SIZE); } /** * 设置磁盘缓存位置,并初始化 * * @param cacheDir * @param cacheSize */ private void setDiskCache(File cacheDir, long cacheSize) { this.mDiskCacheDir = cacheDir; this.mDiskCacheSize = cacheSize; mDiskCacheStarting = true; Log.i(TAG, "mDiskCacheDir:" + mDiskCacheDir); initDiskCache(mDiskCacheDir, mDiskCacheSize); } /** * 初始化磁盘缓存 * * @param diskCacheDir * @param diskCacheSize */ private void initDiskCache(File diskCacheDir, long diskCacheSize) { // Set up disk cache synchronized (mDiskCacheLock) { if (mDiskLruCache == null || mDiskLruCache.isClosed()) { if (diskCacheDir != null) { if (!diskCacheDir.exists()) { diskCacheDir.mkdirs(); } if (getUsableSpace(diskCacheDir) > diskCacheSize) { try { mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, diskCacheSize); if (mDebug) { Log.d(TAG, "Disk cache initialized"); } } catch (final IOException e) { Log.e(TAG, "initDiskCache - " + e); } } } else { // } } mDiskCacheStarting = false; mDiskCacheLock.notifyAll(); } } @Override public Serializable getContent(String context, String id) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap) { contextMap = new HashMap<>(); mContextMaps.put(context, contextMap); } CacheObject obj = contextMap.get(id); if (obj == null) { obj = getContentFromDiskCache(id); if (obj != null) { contextMap.put(id, obj); } } if(obj!=null&&obj.isExpired()){ Log.e(TAG, "is expired & remove "); removeContent(context,id); obj=null; } return obj==null?null:obj.getObject(); } @Override public void getContent(final String context, final String id, final CacheLoader cacheLoader) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap) { contextMap = new HashMap<>(); mContextMaps.put(context, contextMap); } CacheObject obj = contextMap.get(id); if (obj == null) { new AsyncTask() { @Override protected void onPreExecute() { super.onPreExecute(); if (cacheLoader != null) { cacheLoader.loading(); } } @Override protected CacheObject doInBackground(String... params) { return getContentFromDiskCache(params[0]); } @Override protected void onPostExecute(CacheObject result) { super.onPostExecute(result); if (result != null) { addContentToMem(context, id, result); } if(result!=null&&result.isExpired()){ Log.e(TAG, "is expired & remove "); removeContent(context,id); result=null; } if (cacheLoader != null) cacheLoader.returnContent(result==null?null:result.getObject()); } }.execute(id); } else { if(obj!=null&&obj.isExpired()){ Log.e(TAG, "is expired & remove "); removeContent(context,id); obj=null; } if (cacheLoader != null){ cacheLoader.returnContent(obj==null?null:obj.getObject()); } } } @Override public boolean hasContent(String context, String id) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap) { contextMap = new HashMap<>(); mContextMaps.put(context, contextMap); } CacheObject obj = contextMap.get(id); if (obj == null) { return hasContentFromDiskCache(id); } else { if(obj.isExpired()){ Log.e(TAG, "is expired & remove "); removeContent(context,id); return false; }else{ return true; } } } /** * 判断磁盘缓存是否含有 * * @param id * @return */ private boolean hasContentFromDiskCache(String id) { final String key = hashKeyForDisk(id); synchronized (mDiskCacheLock) { while (mDiskCacheStarting) { try { mDiskCacheLock.wait(); } catch (InterruptedException e) { Log.d(e.getMessage()); } } if (mDiskLruCache != null) { InputStream inputStream = null; try { final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot != null) { inputStream = snapshot.getInputStream(DISK_CACHE_INDEX); if (inputStream != null) { CacheObject obj=(CacheObject) Object2FileUtils.readObject(inputStream); if(obj.isExpired()){ Log.e(TAG, "is expired & remove "); mDiskLruCache.remove(hashKeyForDisk(id)); return false; }else{ return true; } } } } catch (final IOException e) { Log.e(TAG, "getContentFromDiskCache - " + e); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { Log.d(e.getMessage()); } } } return false; } } /** * 从磁盘缓存获取 * * @param id * @return */ private CacheObject getContentFromDiskCache(String id) { final String key = hashKeyForDisk(id); synchronized (mDiskCacheLock) { while (mDiskCacheStarting) { try { mDiskCacheLock.wait(); } catch (InterruptedException e) { Log.d(e.getMessage()); } } if (mDiskLruCache != null) { InputStream inputStream = null; try { final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot != null) { inputStream = snapshot.getInputStream(DISK_CACHE_INDEX); if (inputStream != null) { return (CacheObject) Object2FileUtils.readObject(inputStream); } } } catch (final IOException e) { Log.e(TAG, "getContentFromDiskCache - " + e); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { Log.d(e.getMessage()); } } } return null; } } /** * 添加到内存缓存 * * @param context * @param id * @param data */ private void addContentToMem(String context, String id, Serializable data) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap) { contextMap = new HashMap<>(); } contextMap.put(id, new CacheObject(context,id,data)); mContextMaps.put(context, contextMap); } /** * 添加到内存缓存 * @param context * @param id * @param data * @param period */ private void addContentToMem(String context, String id, Serializable data,long period) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap) { contextMap = new HashMap<>(); } contextMap.put(id, new CacheObject(context,id,data,period)); mContextMaps.put(context, contextMap); } /** * 添加到磁盘缓存(也添加到内存缓存) */ @Override public void addContent(String context, String id, Serializable data) { Log.i(TAG, "addContent:" + id + "," + data); removeContent(context, id); addContentToMem(context, id, data); // addContentToDiskCache(id,new CacheObject(context,id,data)); asyncAddContentToDiskCache(id, new CacheObject(context,id,data)); } @Override public void addContent(String context, String id, Serializable data, long period) { Log.i(TAG, "addContent:" + id + "," + data+","+period); removeContent(context, id); addContentToMem(context, id, data,period); // addContentToDiskCache(id,new CacheObject(context,id,data,period)); asyncAddContentToDiskCache(id, new CacheObject(context,id,data,period)); } /** * context暂停或退出时,持久化context关联的缓存(持久化到磁盘) * * @param context */ private void moveContentToDiskCache(String context) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap || contextMap.isEmpty()) { return; } Iterator iterator = contextMap.keySet().iterator(); String id = null; while (iterator.hasNext()) { id = iterator.next(); // addContentToDiskCache(key,contextMap.get(key)); asyncAddContentToDiskCache(id, contextMap.get(id)); } contextMap.clear(); mContextMaps.remove(context); } /** * 异步添加到磁盘缓存 * * @param id * @param data */ private void asyncAddContentToDiskCache(final String id, final CacheObject data) { new AsyncTask() { @Override protected Void doInBackground(Void... params) { addContentToDiskCache(id, data); return null; } }.execute(); } /** * 添加到磁盘缓存 * * @param id * @param data */ private void addContentToDiskCache(String id, CacheObject data) { synchronized (mDiskCacheLock) { // Add to disk cache if (mDiskLruCache != null) { final String key = hashKeyForDisk(id); OutputStream out = null; try { DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot == null) { final DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { out = editor.newOutputStream(DISK_CACHE_INDEX); // 写入out流 Object2FileUtils.writeObject(data, out); editor.commit(); out.close(); flush(); } } else { snapshot.getInputStream(DISK_CACHE_INDEX).close(); } } catch (final IOException e) { Log.e(TAG, "addContentToCache - " + e); } catch (Exception e) { Log.e(TAG, "addContentToCache - " + e); } finally { try { if (out != null) { out.close(); } } catch (IOException e) { Log.d(e.getMessage()); } } } } } @Override public void removeContext(String context) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap || contextMap.isEmpty()) { return; } Iterator iterator = contextMap.keySet().iterator(); String id = null; while (iterator.hasNext()) { id = iterator.next(); String key = hashKeyForDisk(id); try { if (mDiskLruCache != null) { mDiskLruCache.remove(key); } } catch (IOException e) { if (mDebug) { Log.d(TAG, "cache remove" + key, e); } } } contextMap.clear(); mContextMaps.remove(context); } @Override public void removeContent(String context, String id) { HashMap contextMap = mContextMaps.get(context); if (null == contextMap || contextMap.isEmpty()) { return; } contextMap.remove(id); String key = hashKeyForDisk(id); try { if (mDiskLruCache != null) { mDiskLruCache.remove(key); } } catch (IOException e) { if (mDebug) { Log.d(TAG, "cache remove" + key, e); } } } @Override public long size() { long size = 0; synchronized (mDiskCacheLock) { if (mDiskLruCache != null) { size = mDiskLruCache.size(); } } return size; } @Override public void clearCache() { if (mContextMaps != null) { mContextMaps.clear(); if (mDebug) { Log.d(TAG, "Memory cache cleared"); } } synchronized (mDiskCacheLock) { mDiskCacheStarting = true; if (mDiskLruCache != null && !mDiskLruCache.isClosed()) { try { mDiskLruCache.delete(); if (mDebug) { Log.d(TAG, "Disk cache cleared"); } } catch (IOException e) { Log.e(TAG, "clearCache - " + e); } mDiskLruCache = null; initDiskCache(mDiskCacheDir, mDiskCacheSize); } } } @Override public void flush() { synchronized (mDiskCacheLock) { if (mDiskLruCache != null) { try { mDiskLruCache.flush(); if (mDebug) { Log.d(TAG, "Disk cache flushed"); } } catch (IOException e) { Log.e(TAG, "flush - " + e); } } } } @Override public void close() { synchronized (mDiskCacheLock) { if (mDiskLruCache != null) { try { if (!mDiskLruCache.isClosed()) { mDiskLruCache.close(); mDiskLruCache = null; if (mDebug) { Log.d(TAG, "Disk cache closed"); } } } catch (IOException e) { Log.e(TAG, "close - " + e); } } } } private String hashKeyForDisk(String key) { String cacheKey = null; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(key.getBytes("UTF-8")); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(key.hashCode()); } catch (UnsupportedEncodingException e) { Log.e(TAG, e.getMessage()); } return cacheKey; } private String bytesToHexString(byte[] bytes) { // http://stackoverflow.com/questions/332079 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(); } private File getDiskCacheDir(Context context, String uniqueName) { // Check if media is mounted or storage is built-in, if so, try and use // external cache dir // otherwise use internal cache dir String cachePath = null; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !isExternalStorageRemovable()) { cachePath = getExternalCacheDir(context).getPath(); } else { cachePath = context.getCacheDir().getPath(); } return new File(cachePath + File.separator + uniqueName); } @TargetApi(9) private boolean isExternalStorageRemovable() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { return Environment.isExternalStorageRemovable(); } return true; } @TargetApi(8) private File getExternalCacheDir(Context context) { File file = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { file = context.getExternalCacheDir(); } if (file == null) { // Before Froyo we need to construct the external cache dir // ourselves final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/"; file = new File(Environment.getExternalStorageDirectory().getPath() + cacheDir); file.mkdirs(); return file; } return file; } @TargetApi(9) private long getUsableSpace(File path) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { return path.getUsableSpace(); } final StatFs stats = new StatFs(path.getPath()); return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks(); } @Override public String getName() { return TAG; } @Override public void onDestroy() { this.close(); } @Override public void setDebug(boolean debug) { this.mDebug = debug; } @Override public ServiceProperty getServiceProperty() { return mServiceProperty; } @Override public ServiceProperty defaultServiceProperty() { ServiceProperty sp = new ServiceProperty(TAG); sp.putString(CACHE_DIR, "contentCache"); sp.putInt(CACHE_SIZE, 20971520); return sp; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy