mirror of
https://github.com/zhigang1992/AndroidVideoCache.git
synced 2026-04-30 04:45:41 +08:00
add disk usage limits (total cache size, total files, unlimited) #5
This commit is contained in:
@@ -12,7 +12,7 @@ repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
compile 'com.danikula:videocache:2.2.0'
|
||||
compile 'com.danikula:videocache:2.3.0'
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ publish {
|
||||
userOrg = 'alexeydanilov'
|
||||
groupId = 'com.danikula'
|
||||
artifactId = 'videocache'
|
||||
publishVersion = '2.2.0'
|
||||
publishVersion = '2.3.0'
|
||||
description = 'Cache support for android VideoView'
|
||||
website = 'https://github.com/danikula/AndroidVideoCache'
|
||||
}
|
||||
|
||||
30
library/src/main/java/com/danikula/videocache/Config.java
Normal file
30
library/src/main/java/com/danikula/videocache/Config.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import com.danikula.videocache.file.DiskUsage;
|
||||
import com.danikula.videocache.file.FileNameGenerator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Configuration for proxy cache.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
class Config {
|
||||
|
||||
public final File cacheRoot;
|
||||
public final FileNameGenerator fileNameGenerator;
|
||||
public final DiskUsage diskUsage;
|
||||
|
||||
Config(File cacheRoot, FileNameGenerator fileNameGenerator, DiskUsage diskUsage) {
|
||||
this.cacheRoot = cacheRoot;
|
||||
this.fileNameGenerator = fileNameGenerator;
|
||||
this.diskUsage = diskUsage;
|
||||
}
|
||||
|
||||
File generateCacheFile(String url) {
|
||||
String name = fileNameGenerator.generate(url);
|
||||
return new File(cacheRoot, name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.danikula.videocache;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.danikula.videocache.file.FileCache;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import com.danikula.videocache.file.DiskUsage;
|
||||
import com.danikula.videocache.file.FileNameGenerator;
|
||||
import com.danikula.videocache.file.Md5FileNameGenerator;
|
||||
import com.danikula.videocache.file.TotalCountLruDiskUsage;
|
||||
import com.danikula.videocache.file.TotalSizeLruDiskUsage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
@@ -56,11 +64,15 @@ public class HttpProxyCacheServer {
|
||||
private final ServerSocket serverSocket;
|
||||
private final int port;
|
||||
private final Thread waitConnectionThread;
|
||||
private final FileNameGenerator fileNameGenerator;
|
||||
private final Config config;
|
||||
private boolean pinged;
|
||||
|
||||
public HttpProxyCacheServer(FileNameGenerator fileNameGenerator) {
|
||||
this.fileNameGenerator = checkNotNull(fileNameGenerator);
|
||||
public HttpProxyCacheServer(Context context) {
|
||||
this(new Builder(context).buildConfig());
|
||||
}
|
||||
|
||||
private HttpProxyCacheServer(Config config) {
|
||||
this.config = checkNotNull(config);
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getByName(PROXY_HOST);
|
||||
this.serverSocket = new ServerSocket(0, 8, inetAddress);
|
||||
@@ -231,7 +243,7 @@ public class HttpProxyCacheServer {
|
||||
synchronized (clientsLock) {
|
||||
HttpProxyCacheServerClients clients = clientsMap.get(url);
|
||||
if (clients == null) {
|
||||
clients = new HttpProxyCacheServerClients(url, fileNameGenerator);
|
||||
clients = new HttpProxyCacheServerClients(url, config);
|
||||
clientsMap.put(url, clients);
|
||||
}
|
||||
return clients;
|
||||
@@ -328,4 +340,94 @@ public class HttpProxyCacheServer {
|
||||
return pingServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link HttpProxyCacheServer}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private static final long DEFAULT_MAX_SIZE = 512 * 104 * 1024;
|
||||
|
||||
private File cacheRoot;
|
||||
private FileNameGenerator fileNameGenerator;
|
||||
private DiskUsage diskUsage;
|
||||
|
||||
public Builder(Context context) {
|
||||
this.cacheRoot = StorageUtils.getIndividualCacheDirectory(context);
|
||||
this.diskUsage = new TotalSizeLruDiskUsage(DEFAULT_MAX_SIZE);
|
||||
this.fileNameGenerator = new Md5FileNameGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides default cache folder to be used for caching files.
|
||||
* <p/>
|
||||
* By default AndroidVideoCache uses
|
||||
* '/Android/data/[app_package_name]/cache/video-cache/' if card is mounted and app has appropriate permission
|
||||
* or 'video-cache' subdirectory in default application's cache directory otherwise.
|
||||
* <p/>
|
||||
* <b>Note</b> directory must be used <b>only</b> for AndroidVideoCache files.
|
||||
*
|
||||
* @param file a cache directory, can't be null.
|
||||
* @return a builder.
|
||||
*/
|
||||
public Builder cacheDirectory(File file) {
|
||||
this.cacheRoot = checkNotNull(file);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides default cache file name generator {@link Md5FileNameGenerator} .
|
||||
*
|
||||
* @param fileNameGenerator a new file name generator.
|
||||
* @return a builder.
|
||||
*/
|
||||
public Builder fileNameGenerator(FileNameGenerator fileNameGenerator) {
|
||||
this.fileNameGenerator = checkNotNull(fileNameGenerator);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets max cache size in bytes.
|
||||
* All files that exceeds limit will be deleted using LRU strategy.
|
||||
* Default value is 512 Mb.
|
||||
* <p/>
|
||||
* Note this method overrides result of calling {@link #maxCacheFilesCount(int)}
|
||||
*
|
||||
* @param maxSize max cache size in bytes.
|
||||
* @return a builder.
|
||||
*/
|
||||
public Builder maxCacheSize(long maxSize) {
|
||||
this.diskUsage = new TotalSizeLruDiskUsage(maxSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets max cache files count.
|
||||
* All files that exceeds limit will be deleted using LRU strategy.
|
||||
* <p/>
|
||||
* Note this method overrides result of calling {@link #maxCacheSize(long)}
|
||||
*
|
||||
* @param count max cache files count.
|
||||
* @return a builder.
|
||||
*/
|
||||
public Builder maxCacheFilesCount(int count) {
|
||||
this.diskUsage = new TotalCountLruDiskUsage(count);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds new instance of {@link HttpProxyCacheServer}.
|
||||
*
|
||||
* @return proxy cache. Only single instance should be used across whole app.
|
||||
*/
|
||||
public HttpProxyCacheServer build() {
|
||||
Config config = buildConfig();
|
||||
return new HttpProxyCacheServer(config);
|
||||
}
|
||||
|
||||
private Config buildConfig() {
|
||||
return new Config(cacheRoot, fileNameGenerator, diskUsage);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import com.danikula.videocache.file.FileCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
@@ -24,12 +26,12 @@ final class HttpProxyCacheServerClients {
|
||||
private final String url;
|
||||
private volatile HttpProxyCache proxyCache;
|
||||
private final List<CacheListener> listeners = new CopyOnWriteArrayList<>();
|
||||
private final FileNameGenerator fileNameGenerator;
|
||||
private final CacheListener uiCacheListener;
|
||||
private final Config config;
|
||||
|
||||
public HttpProxyCacheServerClients(String url, FileNameGenerator fileNameGenerator) {
|
||||
public HttpProxyCacheServerClients(String url, Config config) {
|
||||
this.url = checkNotNull(url);
|
||||
this.fileNameGenerator = checkNotNull(fileNameGenerator);
|
||||
this.config = checkNotNull(config);
|
||||
this.uiCacheListener = new UiListenerHandler(url, listeners);
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ final class HttpProxyCacheServerClients {
|
||||
|
||||
private HttpProxyCache newHttpProxyCache() throws ProxyCacheException {
|
||||
HttpUrlSource source = new HttpUrlSource(url);
|
||||
FileCache cache = new FileCache(fileNameGenerator.generate(url));
|
||||
FileCache cache = new FileCache(config.generateCacheFile(url), config.diskUsage);
|
||||
HttpProxyCache httpProxyCache = new HttpProxyCache(source, cache);
|
||||
httpProxyCache.registerCacheListener(uiCacheListener);
|
||||
return httpProxyCache;
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
@@ -22,7 +21,7 @@ import static com.danikula.videocache.Preconditions.checkNotNull;
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
class ProxyCacheUtils {
|
||||
public class ProxyCacheUtils {
|
||||
|
||||
static final String LOG_TAG = "ProxyCache";
|
||||
static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
|
||||
@@ -50,19 +49,6 @@ class ProxyCacheUtils {
|
||||
return preview;
|
||||
}
|
||||
|
||||
static void createDirectory(File directory) throws IOException {
|
||||
checkNotNull(directory, "File must be not null!");
|
||||
if (directory.exists()) {
|
||||
checkArgument(directory.isDirectory(), "File is not directory!");
|
||||
} else {
|
||||
boolean isCreated = directory.mkdirs();
|
||||
if (!isCreated) {
|
||||
String error = String.format("Directory %s can't be created", directory.getAbsolutePath());
|
||||
throw new IOException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String encode(String url) {
|
||||
try {
|
||||
return URLEncoder.encode(url, "utf-8");
|
||||
@@ -89,7 +75,7 @@ class ProxyCacheUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static String computeMD5(String string) {
|
||||
public static String computeMD5(String string) {
|
||||
try {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
byte[] digestBytes = messageDigest.digest(string.getBytes());
|
||||
@@ -99,12 +85,11 @@ class ProxyCacheUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static String bytesToHexString(byte[] bytes) {
|
||||
private static String bytesToHexString(byte[] bytes) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static android.os.Environment.MEDIA_MOUNTED;
|
||||
import static com.danikula.videocache.ProxyCacheUtils.LOG_TAG;
|
||||
|
||||
/**
|
||||
* Provides application storage paths
|
||||
* <p/>
|
||||
* See https://github.com/nostra13/Android-Universal-Image-Loader
|
||||
*
|
||||
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class StorageUtils {
|
||||
|
||||
private static final String INDIVIDUAL_DIR_NAME = "video-cache";
|
||||
|
||||
/**
|
||||
* Returns individual application cache directory (for only video caching from Proxy). Cache directory will be
|
||||
* created on SD card <i>("/Android/data/[app_package_name]/cache/video-cache")</i> if card is mounted .
|
||||
* Else - Android defines cache directory on device's file system.
|
||||
*
|
||||
* @param context Application context
|
||||
* @return Cache {@link File directory}
|
||||
*/
|
||||
public static File getIndividualCacheDirectory(Context context) {
|
||||
File cacheDir = getCacheDirectory(context, true);
|
||||
return new File(cacheDir, INDIVIDUAL_DIR_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns application cache directory. Cache directory will be created on SD card
|
||||
* <i>("/Android/data/[app_package_name]/cache")</i> (if card is mounted and app has appropriate permission) or
|
||||
* on device's file system depending incoming parameters.
|
||||
*
|
||||
* @param context Application context
|
||||
* @param preferExternal Whether prefer external location for cache
|
||||
* @return Cache {@link File directory}.<br />
|
||||
* <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
|
||||
* {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
|
||||
*/
|
||||
private static File getCacheDirectory(Context context, boolean preferExternal) {
|
||||
File appCacheDir = null;
|
||||
String externalStorageState;
|
||||
try {
|
||||
externalStorageState = Environment.getExternalStorageState();
|
||||
} catch (NullPointerException e) { // (sh)it happens
|
||||
externalStorageState = "";
|
||||
}
|
||||
if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState)) {
|
||||
appCacheDir = getExternalCacheDir(context);
|
||||
}
|
||||
if (appCacheDir == null) {
|
||||
appCacheDir = context.getCacheDir();
|
||||
}
|
||||
if (appCacheDir == null) {
|
||||
String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
|
||||
Log.w(LOG_TAG, "Can't define system cache directory! '" + cacheDirPath + "%s' will be used.");
|
||||
appCacheDir = new File(cacheDirPath);
|
||||
}
|
||||
return appCacheDir;
|
||||
}
|
||||
|
||||
private static File getExternalCacheDir(Context context) {
|
||||
File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
|
||||
File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
|
||||
if (!appCacheDir.exists()) {
|
||||
if (!appCacheDir.mkdirs()) {
|
||||
Log.w(LOG_TAG, "Unable to create external cache directory");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return appCacheDir;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Declares how {@link FileCache} will use disc space.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
public interface DiskUsage {
|
||||
|
||||
void touch(File file) throws IOException;
|
||||
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package com.danikula.videocache;
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import com.danikula.videocache.Cache;
|
||||
import com.danikula.videocache.ProxyCacheException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import static com.danikula.videocache.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* {@link Cache} that uses file for storing data.
|
||||
*
|
||||
@@ -15,13 +16,22 @@ public class FileCache implements Cache {
|
||||
|
||||
private static final String TEMP_POSTFIX = ".download";
|
||||
|
||||
private final DiskUsage diskUsage;
|
||||
public File file;
|
||||
private RandomAccessFile dataFile;
|
||||
|
||||
public FileCache(File file) throws ProxyCacheException {
|
||||
this(file, new UnlimitedDiskUsage());
|
||||
}
|
||||
|
||||
public FileCache(File file, DiskUsage diskUsage) throws ProxyCacheException {
|
||||
try {
|
||||
checkNotNull(file);
|
||||
ProxyCacheUtils.createDirectory(file.getParentFile());
|
||||
if (diskUsage == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.diskUsage = diskUsage;
|
||||
File directory = file.getParentFile();
|
||||
Files.makeDir(directory);
|
||||
boolean completed = file.exists();
|
||||
this.file = completed ? file : new File(file.getParentFile(), file.getName() + TEMP_POSTFIX);
|
||||
this.dataFile = new RandomAccessFile(this.file, completed ? "r" : "rw");
|
||||
@@ -68,6 +78,7 @@ public class FileCache implements Cache {
|
||||
public synchronized void close() throws ProxyCacheException {
|
||||
try {
|
||||
dataFile.close();
|
||||
diskUsage.touch(file);
|
||||
} catch (IOException e) {
|
||||
throw new ProxyCacheException("Error closing file " + file, e);
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import java.io.File;
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
/**
|
||||
* Generator for files to be used for caching.
|
||||
@@ -9,6 +7,6 @@ import java.io.File;
|
||||
*/
|
||||
public interface FileNameGenerator {
|
||||
|
||||
File generate(String url);
|
||||
String generate(String url);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utils for work with files.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
class Files {
|
||||
|
||||
static void makeDir(File directory) throws IOException {
|
||||
if (directory.exists()) {
|
||||
if (!directory.isDirectory()) {
|
||||
throw new IOException("File " + directory + " is not directory!");
|
||||
}
|
||||
} else {
|
||||
boolean isCreated = directory.mkdirs();
|
||||
if (!isCreated) {
|
||||
throw new IOException(String.format("Directory %s can't be created", directory.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<File> getLruListFiles(File directory) {
|
||||
List<File> result = new LinkedList<>();
|
||||
File[] files = directory.listFiles();
|
||||
if (files != null) {
|
||||
result = Arrays.asList(files);
|
||||
Collections.sort(result, new LastModifiedComparator());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void setLastModifiedNow(File file) throws IOException {
|
||||
if (file.exists()) {
|
||||
long now = System.currentTimeMillis();
|
||||
boolean modified = file.setLastModified(now); // on some devices (e.g. Nexus 5) doesn't work
|
||||
if (!modified) {
|
||||
modify(file);
|
||||
if (file.lastModified() < now) {
|
||||
throw new IOException("Error set last modified date to " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void modify(File file) throws IOException {
|
||||
long size = file.length();
|
||||
if (size == 0) {
|
||||
recreateZeroSizeFile(file);
|
||||
return;
|
||||
}
|
||||
|
||||
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
|
||||
accessFile.seek(size - 1);
|
||||
byte lastByte = accessFile.readByte();
|
||||
accessFile.seek(size - 1);
|
||||
accessFile.write(lastByte);
|
||||
accessFile.close();
|
||||
}
|
||||
|
||||
private static void recreateZeroSizeFile(File file) throws IOException {
|
||||
if (!file.delete() || !file.createNewFile()) {
|
||||
throw new IOException("Error recreate zero-size file " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LastModifiedComparator implements Comparator<File> {
|
||||
|
||||
@Override
|
||||
public int compare(File lhs, File rhs) {
|
||||
return compareLong(lhs.lastModified(), rhs.lastModified());
|
||||
}
|
||||
|
||||
private int compareLong(long first, long second) {
|
||||
return (first < second) ? -1 : ((first == second) ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* {@link DiskUsage} that uses LRU (Least Recently Used) strategy to trim cache.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
abstract class LruDiskUsage implements DiskUsage {
|
||||
|
||||
private static final String LOG_TAG = "ProxyCache";
|
||||
private final ExecutorService workerThread = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
public void touch(File file) throws IOException {
|
||||
workerThread.submit(new TouchCallable(file));
|
||||
}
|
||||
|
||||
private void touchInBackground(File file) throws IOException {
|
||||
Files.setLastModifiedNow(file);
|
||||
List<File> files = Files.getLruListFiles(file.getParentFile());
|
||||
trim(files);
|
||||
}
|
||||
|
||||
protected abstract boolean accept(File file, long totalSize, int totalCount);
|
||||
|
||||
private void trim(List<File> files) {
|
||||
long totalSize = countTotalSize(files);
|
||||
int totalCount = files.size();
|
||||
for (File file : files) {
|
||||
boolean accepted = accept(file, totalSize, totalCount);
|
||||
if (!accepted) {
|
||||
long fileSize = file.length();
|
||||
boolean deleted = file.delete();
|
||||
if (deleted) {
|
||||
totalCount--;
|
||||
totalSize -= fileSize;
|
||||
Log.i(LOG_TAG, "Cache file " + file + " is deleted because it exceeds cache limit");
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Error deleting file " + file + " for trimming cache");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long countTotalSize(List<File> files) {
|
||||
long totalSize = 0;
|
||||
for (File file : files) {
|
||||
totalSize += file.length();
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
private class TouchCallable implements Callable<Void> {
|
||||
|
||||
private final File file;
|
||||
|
||||
public TouchCallable(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
touchInBackground(file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.danikula.videocache;
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static com.danikula.videocache.Preconditions.checkNotNull;
|
||||
import com.danikula.videocache.ProxyCacheUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link FileNameGenerator} that uses MD5 of url as file name
|
||||
@@ -14,19 +12,12 @@ import static com.danikula.videocache.Preconditions.checkNotNull;
|
||||
public class Md5FileNameGenerator implements FileNameGenerator {
|
||||
|
||||
private static final int MAX_EXTENSION_LENGTH = 4;
|
||||
private final File cacheDirectory;
|
||||
|
||||
public Md5FileNameGenerator(File cacheDirectory) {
|
||||
this.cacheDirectory = checkNotNull(cacheDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File generate(String url) {
|
||||
checkNotNull(url);
|
||||
public String generate(String url) {
|
||||
String extension = getExtension(url);
|
||||
String name = ProxyCacheUtils.computeMD5(url);
|
||||
name = TextUtils.isEmpty(extension) ? name : name + "." + extension;
|
||||
return new File(cacheDirectory, name);
|
||||
return TextUtils.isEmpty(extension) ? name : name + "." + extension;
|
||||
}
|
||||
|
||||
private String getExtension(String url) {
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* {@link DiskUsage} that uses LRU (Least Recently Used) strategy and trims cache size to max files count if needed.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
public class TotalCountLruDiskUsage extends LruDiskUsage {
|
||||
|
||||
private final int maxCount;
|
||||
|
||||
public TotalCountLruDiskUsage(int maxCount) {
|
||||
if (maxCount <= 0) {
|
||||
throw new IllegalArgumentException("Max count must be positive number!");
|
||||
}
|
||||
this.maxCount = maxCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean accept(File file, long totalSize, int totalCount) {
|
||||
return totalCount <= maxCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* {@link DiskUsage} that uses LRU (Least Recently Used) strategy and trims cache size to max size if needed.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
public class TotalSizeLruDiskUsage extends LruDiskUsage {
|
||||
|
||||
private final long maxSize;
|
||||
|
||||
public TotalSizeLruDiskUsage(long maxSize) {
|
||||
if (maxSize <= 0) {
|
||||
throw new IllegalArgumentException("Max size must be positive number!");
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean accept(File file, long totalSize, int totalCount) {
|
||||
return totalSize <= maxSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unlimited version of {@link DiskUsage}.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
public class UnlimitedDiskUsage implements DiskUsage {
|
||||
|
||||
@Override
|
||||
public void touch(File file) throws IOException {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -35,10 +35,10 @@ apt {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// compile project(':library')
|
||||
compile project(':library')
|
||||
compile 'com.android.support:support-v4:23.1.0'
|
||||
compile 'org.androidannotations:androidannotations-api:3.3.2'
|
||||
compile 'com.danikula:videocache:2.2.0'
|
||||
// compile 'com.danikula:videocache:2.2.0'
|
||||
compile 'com.viewpagerindicator:library:2.4.2-SNAPSHOT@aar'
|
||||
apt 'org.androidannotations:androidannotations:3.3.2'
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ package com.danikula.videocache.sample;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import com.danikula.videocache.FileNameGenerator;
|
||||
import com.danikula.videocache.HttpProxyCacheServer;
|
||||
import com.danikula.videocache.Md5FileNameGenerator;
|
||||
|
||||
/**
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
@@ -20,7 +18,6 @@ public class App extends Application {
|
||||
}
|
||||
|
||||
private HttpProxyCacheServer newProxy() {
|
||||
FileNameGenerator nameGenerator = new Md5FileNameGenerator(getExternalCacheDir());
|
||||
return new HttpProxyCacheServer(nameGenerator);
|
||||
return new HttpProxyCacheServer(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import com.danikula.videocache.file.FileNameGenerator;
|
||||
import com.danikula.videocache.file.Md5FileNameGenerator;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -62,21 +64,16 @@ public class FileNameGeneratorTest {
|
||||
assertThat(path).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAssertNullRoot() throws Exception {
|
||||
new Md5FileNameGenerator(null);
|
||||
fail("Root folder should be not null");
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAssertNullUrl() throws Exception {
|
||||
FileNameGenerator nameGenerator = new Md5FileNameGenerator(new File("/"));
|
||||
FileNameGenerator nameGenerator = new Md5FileNameGenerator();
|
||||
nameGenerator.generate(null);
|
||||
fail("Url should be not null");
|
||||
}
|
||||
|
||||
private String generateMd5Name(String rootFolder, String url) {
|
||||
FileNameGenerator nameGenerator = new Md5FileNameGenerator(new File(rootFolder));
|
||||
return nameGenerator.generate(url).getAbsolutePath();
|
||||
FileNameGenerator nameGenerator = new Md5FileNameGenerator();
|
||||
String name = nameGenerator.generate(url);
|
||||
return new File(rootFolder, name).getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ package com.danikula.videocache;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.danikula.android.garden.io.IoUtils;
|
||||
import com.danikula.videocache.file.FileNameGenerator;
|
||||
import com.danikula.videocache.file.Md5FileNameGenerator;
|
||||
import com.danikula.videocache.support.ProxyCacheTestUtils;
|
||||
import com.danikula.videocache.support.Response;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricGradleTestRunner;
|
||||
@@ -17,12 +20,18 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.danikula.android.garden.io.Files.cleanDirectory;
|
||||
import static com.danikula.android.garden.io.Files.createDirectory;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.ASSETS_DATA_BIG_NAME;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.ASSETS_DATA_NAME;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_BIG_SIZE;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_BIG_URL;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_BIG_URL_ONE_REDIRECT;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_SIZE;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL_3_REDIRECTS;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL_6_REDIRECTS;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL_ONE_REDIRECT;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.getFileContent;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.loadAssetFile;
|
||||
import static com.danikula.videocache.support.ProxyCacheTestUtils.readProxyResponse;
|
||||
@@ -35,6 +44,15 @@ import static org.fest.assertions.api.Assertions.assertThat;
|
||||
@Config(constants = BuildConfig.class, emulateSdk = BuildConfig.MIN_SDK_VERSION)
|
||||
public class HttpProxyCacheServerTest {
|
||||
|
||||
private File cacheFolder;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
cacheFolder = ProxyCacheTestUtils.newCacheFile();
|
||||
createDirectory(cacheFolder);
|
||||
cleanDirectory(cacheFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpProxyCache() throws Exception {
|
||||
Pair<File, Response> response = readProxyData(HTTP_DATA_URL);
|
||||
@@ -46,14 +64,14 @@ public class HttpProxyCacheServerTest {
|
||||
|
||||
@Test
|
||||
public void testProxyContentWithPartialCache() throws Exception {
|
||||
FileNameGenerator fileNameGenerator = new Md5FileNameGenerator(RuntimeEnvironment.application.getExternalCacheDir());
|
||||
File file = fileNameGenerator.generate(HTTP_DATA_URL);
|
||||
File cacheDir = RuntimeEnvironment.application.getExternalCacheDir();
|
||||
File file = new File(cacheDir, new Md5FileNameGenerator().generate(HTTP_DATA_URL));
|
||||
int partialCacheSize = 1000;
|
||||
byte[] partialData = ProxyCacheTestUtils.generate(partialCacheSize);
|
||||
File partialCacheFile = ProxyCacheTestUtils.getTempFile(file);
|
||||
IoUtils.saveToFile(partialData, partialCacheFile);
|
||||
|
||||
HttpProxyCacheServer proxy = new HttpProxyCacheServer(fileNameGenerator);
|
||||
HttpProxyCacheServer proxy = newProxy(cacheDir);
|
||||
Response response = readProxyResponse(proxy, HTTP_DATA_URL);
|
||||
proxy.shutdown();
|
||||
|
||||
@@ -132,11 +150,70 @@ public class HttpProxyCacheServerTest {
|
||||
assertThat(getFileContent(response.first)).isEqualTo(loadAssetFile(ASSETS_DATA_BIG_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxSizeCacheLimit() throws Exception {
|
||||
HttpProxyCacheServer proxy = new HttpProxyCacheServer.Builder(RuntimeEnvironment.application)
|
||||
.cacheDirectory(cacheFolder)
|
||||
.maxCacheSize(HTTP_DATA_SIZE * 3 - 1) // for 2 files
|
||||
.build();
|
||||
|
||||
// use different url (doesn't matter than same content)
|
||||
readProxyResponse(proxy, HTTP_DATA_URL, 0);
|
||||
Thread.sleep(1050); // wait for new last modified date (file rounds time to second)
|
||||
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_ONE_REDIRECT, 0);
|
||||
Thread.sleep(1050);
|
||||
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_3_REDIRECTS, 0);
|
||||
Thread.sleep(1050);
|
||||
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL)).doesNotExist();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_ONE_REDIRECT)).exists();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_3_REDIRECTS)).exists();
|
||||
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_ONE_REDIRECT, 0); // touch file
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_6_REDIRECTS, 0);
|
||||
proxy.shutdown();
|
||||
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_3_REDIRECTS)).doesNotExist();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_ONE_REDIRECT)).exists();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_6_REDIRECTS)).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxFileCacheLimit() throws Exception {
|
||||
HttpProxyCacheServer proxy = new HttpProxyCacheServer.Builder(RuntimeEnvironment.application)
|
||||
.cacheDirectory(cacheFolder)
|
||||
.maxCacheFilesCount(2)
|
||||
.build();
|
||||
|
||||
// use different url (doesn't matter than same content)
|
||||
readProxyResponse(proxy, HTTP_DATA_URL, 0);
|
||||
Thread.sleep(1050); // wait for new last modified date (file rounds time to second)
|
||||
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_ONE_REDIRECT, 0);
|
||||
Thread.sleep(1050);
|
||||
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_3_REDIRECTS, 0);
|
||||
Thread.sleep(1050);
|
||||
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL)).doesNotExist();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_ONE_REDIRECT)).exists();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_3_REDIRECTS)).exists();
|
||||
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_ONE_REDIRECT, 0); // touch file
|
||||
readProxyResponse(proxy, HTTP_DATA_URL_6_REDIRECTS, 0);
|
||||
proxy.shutdown();
|
||||
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_3_REDIRECTS)).doesNotExist();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_ONE_REDIRECT)).exists();
|
||||
assertThat(file(cacheFolder, HTTP_DATA_URL_6_REDIRECTS)).exists();
|
||||
}
|
||||
|
||||
private Pair<File, Response> readProxyData(String url, int offset) throws IOException {
|
||||
File externalCacheDir = RuntimeEnvironment.application.getExternalCacheDir();
|
||||
FileNameGenerator fileNameGenerator = new Md5FileNameGenerator(externalCacheDir);
|
||||
File file = fileNameGenerator.generate(url);
|
||||
HttpProxyCacheServer proxy = new HttpProxyCacheServer(fileNameGenerator);
|
||||
File file = file(externalCacheDir, url);
|
||||
HttpProxyCacheServer proxy = newProxy(externalCacheDir);
|
||||
|
||||
Response response = readProxyResponse(proxy, url, offset);
|
||||
proxy.shutdown();
|
||||
@@ -144,7 +221,19 @@ public class HttpProxyCacheServerTest {
|
||||
return new Pair<>(file, response);
|
||||
}
|
||||
|
||||
private File file(File parent, String url) {
|
||||
FileNameGenerator fileNameGenerator = new Md5FileNameGenerator();
|
||||
String name = fileNameGenerator.generate(url);
|
||||
return new File(parent, name);
|
||||
}
|
||||
|
||||
private Pair<File, Response> readProxyData(String url) throws IOException {
|
||||
return readProxyData(url, -1);
|
||||
}
|
||||
|
||||
private HttpProxyCacheServer newProxy(File cacheDir) {
|
||||
return new HttpProxyCacheServer.Builder(RuntimeEnvironment.application)
|
||||
.cacheDirectory(cacheDir)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import com.danikula.videocache.file.FileCache;
|
||||
import com.danikula.videocache.support.ProxyCacheTestUtils;
|
||||
import com.danikula.videocache.support.Response;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.danikula.videocache;
|
||||
|
||||
import com.danikula.android.garden.io.IoUtils;
|
||||
import com.danikula.videocache.file.FileCache;
|
||||
import com.danikula.videocache.support.AngryHttpUrlSource;
|
||||
import com.danikula.videocache.support.PhlegmaticByteArraySource;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import com.danikula.videocache.support.ProxyCacheTestUtils;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricGradleTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.danikula.android.garden.io.Files.cleanDirectory;
|
||||
import static com.danikula.android.garden.io.Files.createDirectory;
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for implementations of {@link DiskUsage}.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
@RunWith(RobolectricGradleTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, emulateSdk = BuildConfig.MIN_SDK_VERSION)
|
||||
public class DiskUsageTest {
|
||||
|
||||
private File cacheFolder;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
cacheFolder = ProxyCacheTestUtils.newCacheFile();
|
||||
createDirectory(cacheFolder);
|
||||
cleanDirectory(cacheFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxSizeCacheLimit() throws Exception {
|
||||
DiskUsage diskUsage = new TotalSizeLruDiskUsage(300);
|
||||
long now = System.currentTimeMillis();
|
||||
createFile(file("b"), 101, now - 10000);
|
||||
createFile(file("c"), 102, now - 8000);
|
||||
createFile(file("a"), 104, now - 4000); // exceeds
|
||||
|
||||
diskUsage.touch(file("c"));
|
||||
waitForAsyncTrimming();
|
||||
|
||||
assertThat(file("b")).doesNotExist();
|
||||
assertThat(file("c")).exists();
|
||||
assertThat(file("a")).exists();
|
||||
|
||||
createFile(file("d"), 500, now); // exceeds all
|
||||
diskUsage.touch(file("d"));
|
||||
waitForAsyncTrimming();
|
||||
|
||||
assertThat(file("a")).doesNotExist();
|
||||
assertThat(file("c")).doesNotExist();
|
||||
assertThat(file("d")).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxFilesCount() throws Exception {
|
||||
DiskUsage diskUsage = new TotalCountLruDiskUsage(2);
|
||||
long now = System.currentTimeMillis();
|
||||
createFile(file("b"), 101, now - 10000);
|
||||
createFile(file("c"), 102, now - 8000);
|
||||
createFile(file("a"), 104, now - 4000);
|
||||
|
||||
diskUsage.touch(file("c"));
|
||||
waitForAsyncTrimming();
|
||||
|
||||
assertThat(file("b")).doesNotExist();
|
||||
assertThat(file("a")).exists();
|
||||
assertThat(file("c")).exists();
|
||||
|
||||
createFile(file("d"), 500, now);
|
||||
diskUsage.touch(file("d"));
|
||||
waitForAsyncTrimming();
|
||||
|
||||
assertThat(file("a")).doesNotExist();
|
||||
assertThat(file("c")).exists();
|
||||
assertThat(file("d")).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTouch() throws Exception {
|
||||
DiskUsage diskUsage = new TotalCountLruDiskUsage(2);
|
||||
long now = System.currentTimeMillis();
|
||||
createFile(file("b"), 101, now - 10000);
|
||||
createFile(file("c"), 102, now - 8000);
|
||||
createFile(file("a"), 104, now - 4000);
|
||||
|
||||
diskUsage.touch(file("b"));
|
||||
waitForAsyncTrimming();
|
||||
|
||||
assertThat(file("b")).exists();
|
||||
assertThat(file("a")).exists();
|
||||
assertThat(file("c")).doesNotExist();
|
||||
|
||||
Thread.sleep(1000); // last modified is rounded to seconds, so wait for sec
|
||||
new TotalCountLruDiskUsage(1).touch(file("a"));
|
||||
waitForAsyncTrimming();
|
||||
|
||||
assertThat(file("a")).exists();
|
||||
assertThat(file("b")).doesNotExist();
|
||||
}
|
||||
|
||||
private void waitForAsyncTrimming() throws InterruptedException {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
private File file(String name) {
|
||||
return new File(cacheFolder, name);
|
||||
}
|
||||
|
||||
private void createFile(File file, int capacity, long lastModified) throws IOException {
|
||||
byte[] data = ProxyCacheTestUtils.generate(capacity);
|
||||
com.google.common.io.Files.write(data, file);
|
||||
boolean modified = file.setLastModified(lastModified);
|
||||
assertThat(modified).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.danikula.videocache;
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import com.danikula.android.garden.io.Files;
|
||||
import com.danikula.android.garden.io.IoUtils;
|
||||
import com.danikula.videocache.Cache;
|
||||
import com.danikula.videocache.ProxyCacheException;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
import org.junit.Assert;
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.danikula.videocache.file;
|
||||
|
||||
import com.danikula.videocache.support.ProxyCacheTestUtils;
|
||||
import com.danikula.videocache.test.BuildConfig;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricGradleTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Files}.
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
@RunWith(RobolectricGradleTestRunner.class)
|
||||
@Config(constants = BuildConfig.class, emulateSdk = BuildConfig.MIN_SDK_VERSION)
|
||||
public class FilesTest {
|
||||
|
||||
@Test
|
||||
public void testModify() throws Exception {
|
||||
byte[] data = ProxyCacheTestUtils.generate(999);
|
||||
File file = ProxyCacheTestUtils.newCacheFile();
|
||||
com.google.common.io.Files.write(data, file);
|
||||
long lastModified = file.lastModified();
|
||||
|
||||
Thread.sleep(1100); // file can store modification date in seconds. so wait for ~ 1 sec
|
||||
Files.modify(file);
|
||||
|
||||
assertThat(file).hasBinaryContent(data);
|
||||
assertThat(file.lastModified()).isGreaterThan(lastModified);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetModifiedNow() throws Exception {
|
||||
File file = ProxyCacheTestUtils.newCacheFile();
|
||||
com.google.common.io.Files.write(ProxyCacheTestUtils.generate(22), file);
|
||||
|
||||
Thread.sleep(1100); // file can store modification date in seconds. so wait for ~ 1 sec
|
||||
long nowRoundedToSecond = System.currentTimeMillis() / 1000 * 1000;
|
||||
Files.setLastModifiedNow(file);
|
||||
|
||||
assertThat(file.lastModified()).isGreaterThanOrEqualTo(nowRoundedToSecond);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyZeroSizeFile() throws Exception {
|
||||
File file = ProxyCacheTestUtils.newCacheFile();
|
||||
boolean created = file.createNewFile();
|
||||
assertThat(created).isTrue();
|
||||
|
||||
Thread.sleep(1100); // file can store modification date in seconds. so wait for ~ 2 sec
|
||||
long nowRoundedToSecond = System.currentTimeMillis() / 1000 * 1000;
|
||||
Files.modify(file);
|
||||
|
||||
assertThat(file.lastModified()).isGreaterThanOrEqualTo(nowRoundedToSecond);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.danikula.videocache.Source;
|
||||
*
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
@Deprecated // use Mockito to throw error
|
||||
public class AngryHttpUrlSource implements Source {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.Random;
|
||||
/**
|
||||
* @author Alexey Danilov (danikula@gmail.com).
|
||||
*/
|
||||
@Deprecated // TODO: use Mockito to mock delay
|
||||
public class PhlegmaticByteArraySource extends ByteArraySource {
|
||||
|
||||
private final Random delayGenerator;
|
||||
|
||||
Reference in New Issue
Block a user