URLcache

package eu.nextap.cache; import android.content.Context; import android.os.Environment; import android.util.Log; import android.webkit.WebResourceResponse; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static eu.nextap.cache.CacheEntryBehaviour.CACHE_ALL; import static eu.nextap.cache.CacheEntryProcess.*; import static eu.nextap.cache.CacheStorage.USE_INTERNAL_STORAGE; import static eu.nextap.cache.FileUtils.copyAndSaveStreams; import static eu.nextap.cache.FileUtils.getMimeType; /** * All processes of caching like saving, loading or editing, * * @author Tomáš Vítek */ public class UrlCache { /** * Define time constants how long will be cache saved. */ public static final long ONE_SECOND = 1000L; public static final long ONE_MINUTE = 60L * ONE_SECOND; public static final long ONE_HOUR = 60L * ONE_MINUTE; public static final long ONE_DAY = 24 * ONE_HOUR; public static final long ONE_WEEK = 7 * ONE_DAY; public static final long ONE_MONTH = 4 * ONE_WEEK; public static final long ONE_YEAR = 12 * ONE_MONTH; public static final long INFINITY = 100000000 * ONE_YEAR; public static final String ENCODING = "UTF-8"; /** * Class name. */ private static String TAG = "UrlCache"; /** * Define other variables. */ private CacheEntryBehaviour cacheEntryBehaviour = CACHE_ALL; private CacheStorage cacheStorage = USE_INTERNAL_STORAGE; private long maxAgeMillis; /** * Define objects. */ private Map<String, CacheEntry> cacheEntries = new HashMap<String, CacheEntry>(); private Context context; private File rootDir; private PathGenerator pathGenerator; /** * Set thread to load web and in background download files. */ int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor executor = new ThreadPoolExecutor(NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); /** * UrlCache constructor which will detect if we want to save files on internal or external directory. */ public UrlCache(Context context) { this.context = context; executor.allowsCoreThreadTimeOut(); setFilesDirectory(getCacheStorage()); setPathGenerator(new UrlPathGenerator()); } /** * Register new file in cache so create new path and generate cacheEntry. */ public void registerCacheEntry(String url, String mimeType, String encoding) { //Create unique path. String filePath = this.rootDir.getPath() + File.separator + pathGenerator.createPath(url); //Create new CacheEntry. CacheEntry entry = new CacheEntry(url, filePath, mimeType, encoding); //Put url and CacheEntry into CacheEntries Map. this.cacheEntries.put(url, entry); } /** * Load saved cache file or if cache file isnt available download and save it again. */ public WebResourceResponse load(final String url) { //Get current CacheEntry. CacheEntry cacheEntry = this.cacheEntries.get(url); //Set when will be cache removed. setMaxAgeMillis(ONE_HOUR); //Check if cacheEntry isn't null. if (cacheEntry == null) { switch (getCacheEntryBehaviour()) { case CACHE_ALL: //Create new cacheEntry. registerCacheEntry(url, getMimeType(url), ENCODING); cacheEntry = this.cacheEntries.get(url); break; case USE_CACHE_ENTRY: return null; default: throw new IllegalArgumentException("Unknown CacheEntryBehaviour"); } } //Find file in definite path. File cachedFile = new File(cacheEntry.filePath); //Check if cachedFile doesn't exists. if (cachedFile.exists()) { //Define new cacheEntry age. long cacheEntryAge = System.currentTimeMillis() - cachedFile.lastModified(); //Check if new cacheEntry age isn't bigger than cacheEntry's max age. if (cacheEntryAge > maxAgeMillis) { //CacheEntry is not downloaded because time expires cacheEntry.setCacheEntryProcess(ERROR); //Cached file deleted. cachedFile.delete(); //Call load() again. return load(url); } //Cached file exists and is not too old so try to return File. try { cacheEntry.setCacheEntryProcess(DOWNLOADED); return new WebResourceResponse(cacheEntry.mimeType, cacheEntry.encoding, new FileInputStream(cachedFile)); } catch (FileNotFoundException e) { cacheEntry.setCacheEntryProcess(ERROR); Log.e(TAG, e.getMessage(), e); } } else { cacheEntry.setCacheEntryProcess(ERROR); try { cacheEntry.setCacheEntryProcess(SCHEDULED); executor.execute(new DownloadInBackground(url, cacheEntry)); } catch (Exception e) { Log.e(TAG, e.getMessage(), e); } } return null; } /** * Download current file and save it into cache. */ private void downloadAndStore(String url, CacheEntry cacheEntry) throws IOException { Log.d(TAG, "Downloading " + url); cacheEntry.setCacheEntryProcess(DOWNLOADING); //Define objects. URL urlObj = new URL(url); URLConnection urlConnection = urlObj.openConnection(); //Create new file from cacheEntry path. File file = new File(cacheEntry.filePath); //Configure path. if (!file.exists()) { file.getParentFile().mkdirs(); } //Open cached file. FileOutputStream fileOutputStream = new FileOutputStream(file); InputStream urlInput = urlConnection.getInputStream(); copyAndSaveStreams(fileOutputStream, urlInput); //Close input and output streams. urlInput.close(); fileOutputStream.close(); cacheEntry.setCacheEntryProcess(DOWNLOADED); Log.d(TAG, "Saved " + url); } /** * Will set where will be files saved. */ private void setFilesDirectory(CacheStorage cacheStorage) { switch (cacheStorage) { case USE_EXTERNAL_STORAGE: this.rootDir = Environment.getExternalStorageDirectory(); break; case USE_INTERNAL_STORAGE: this.rootDir = this.context.getFilesDir(); break; } } public void removeRootDirectory(String url) { this.pathGenerator.removeRootDirectory(url); } public void setCacheEntryBehaviour(CacheEntryBehaviour cacheEntryBehaviour) { this.cacheEntryBehaviour = cacheEntryBehaviour; } /** * Getters and setters. */ public CacheEntryBehaviour getCacheEntryBehaviour() { return cacheEntryBehaviour; } public CacheStorage getCacheStorage() { return cacheStorage; } public void setCacheStorage(CacheStorage cacheStorage) { this.cacheStorage = cacheStorage; } public PathGenerator getPathGenerator() { return pathGenerator; } public void setPathGenerator(PathGenerator pathGenerator) { this.pathGenerator = pathGenerator; } public long getMaxAgeMillis() { return maxAgeMillis; } public void setMaxAgeMillis(long maxAgeMillis) { this.maxAgeMillis = maxAgeMillis; } /** * Will download files in background during web will be showed. */ private class DownloadInBackground implements Runnable { private String url; private CacheEntry finalCacheEntry; DownloadInBackground(String url, CacheEntry cacheEntry) { this.url = url; this.finalCacheEntry = cacheEntry; } @Override public void run() { //Download and save into cache. try { downloadAndStore(url, finalCacheEntry); } catch (IOException e) { Log.e(TAG, e.getMessage()); } } } /** * CacheEntry is used to save one file from web, so we will use probably many cacheEntries. */ private static class CacheEntry { /** * Declare variables. */ private String url; private String filePath; private String mimeType; private String encoding; private CacheEntryProcess cacheEntryProcess; /** * CacheEntry constructor. */ private CacheEntry(String url, String filePath, String mimeType, String encoding) { //Define variables. this.url = url; this.filePath = filePath; this.mimeType = mimeType; this.encoding = encoding; } public CacheEntryProcess getCacheEntryProcess() { return cacheEntryProcess; } public void setCacheEntryProcess(CacheEntryProcess cacheEntryProcess) { this.cacheEntryProcess = cacheEntryProcess; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getMimeType() { return mimeType; } public void setMimeType(String mimeType) { this.mimeType = mimeType; } public String getEncoding() { return encoding; } public void setEncoding(String encoding) { this.encoding = encoding; } } }

Be the first to comment

You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.