Reduce the number of cache files
- Increase the default cache file size to 5MB - Recommended a minimum cache file size of 2MB to discourage applications from specifying values small enough such that unreasonably large numbers of cache files are generated - Allow maxCacheFileSize=C.LENGTH_UNSET, equivalent to setting it to MAX_VALUE. This is just for API consistency with other APIs we have that accept LENGTH_UNSET Issue: #4253 PiperOrigin-RevId: 227524233
This commit is contained in:
parent
ae65bcecd4
commit
0ed7ddecd7
@ -23,6 +23,7 @@ import com.google.android.exoplayer2.upstream.DummyDataSource;
|
|||||||
import com.google.android.exoplayer2.upstream.FileDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.FileDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.upstream.PriorityDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.PriorityDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.Cache;
|
import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSink;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSinkFactory;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSinkFactory;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||||
@ -111,7 +112,7 @@ public final class DownloaderConstructorHelper {
|
|||||||
DataSink.Factory writeDataSinkFactory =
|
DataSink.Factory writeDataSinkFactory =
|
||||||
cacheWriteDataSinkFactory != null
|
cacheWriteDataSinkFactory != null
|
||||||
? cacheWriteDataSinkFactory
|
? cacheWriteDataSinkFactory
|
||||||
: new CacheDataSinkFactory(cache, CacheDataSource.DEFAULT_MAX_CACHE_FILE_SIZE);
|
: new CacheDataSinkFactory(cache, CacheDataSink.DEFAULT_MAX_CACHE_FILE_SIZE);
|
||||||
onlineCacheDataSourceFactory =
|
onlineCacheDataSourceFactory =
|
||||||
new CacheDataSourceFactory(
|
new CacheDataSourceFactory(
|
||||||
cache,
|
cache,
|
||||||
|
@ -20,6 +20,7 @@ import com.google.android.exoplayer2.upstream.DataSink;
|
|||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.ReusableBufferedOutputStream;
|
import com.google.android.exoplayer2.util.ReusableBufferedOutputStream;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -36,8 +37,13 @@ import java.io.OutputStream;
|
|||||||
*/
|
*/
|
||||||
public final class CacheDataSink implements DataSink {
|
public final class CacheDataSink implements DataSink {
|
||||||
|
|
||||||
|
/** Default {@code maxCacheFileSize} recommended for caching use cases. */
|
||||||
|
public static final long DEFAULT_MAX_CACHE_FILE_SIZE = 5 * 1024 * 1024;
|
||||||
/** Default buffer size in bytes. */
|
/** Default buffer size in bytes. */
|
||||||
public static final int DEFAULT_BUFFER_SIZE = 20480;
|
public static final int DEFAULT_BUFFER_SIZE = 20 * 1024;
|
||||||
|
|
||||||
|
private static final long MIN_RECOMMENDED_MAX_CACHE_FILE_SIZE = 2 * 1024 * 1024;
|
||||||
|
private static final String TAG = "CacheDataSink";
|
||||||
|
|
||||||
private final Cache cache;
|
private final Cache cache;
|
||||||
private final long maxCacheFileSize;
|
private final long maxCacheFileSize;
|
||||||
@ -64,12 +70,15 @@ public final class CacheDataSink implements DataSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a CacheDataSink using the {@link #DEFAULT_BUFFER_SIZE}.
|
* Constructs an instance using {@link #DEFAULT_BUFFER_SIZE}.
|
||||||
*
|
*
|
||||||
* @param cache The cache into which data should be written.
|
* @param cache The cache into which data should be written.
|
||||||
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the sink is opened for a
|
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If a request results in
|
||||||
* {@link DataSpec} whose size exceeds this value, then the data will be fragmented into
|
* data being written whose size exceeds this value, then the data will be fragmented into
|
||||||
* multiple cache files.
|
* multiple cache files. If set to {@link C#LENGTH_UNSET} then no fragmentation will occur.
|
||||||
|
* Using a small value allows for finer-grained cache eviction policies, at the cost of
|
||||||
|
* increased overhead both on the cache implementation and the file system. Values under
|
||||||
|
* {@code (2 * 1024 * 1024)} are not recommended.
|
||||||
*/
|
*/
|
||||||
public CacheDataSink(Cache cache, long maxCacheFileSize) {
|
public CacheDataSink(Cache cache, long maxCacheFileSize) {
|
||||||
this(cache, maxCacheFileSize, DEFAULT_BUFFER_SIZE);
|
this(cache, maxCacheFileSize, DEFAULT_BUFFER_SIZE);
|
||||||
@ -77,15 +86,29 @@ public final class CacheDataSink implements DataSink {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cache The cache into which data should be written.
|
* @param cache The cache into which data should be written.
|
||||||
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the sink is opened for a
|
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If a request results in
|
||||||
* {@link DataSpec} whose size exceeds this value, then the data will be fragmented into
|
* data being written whose size exceeds this value, then the data will be fragmented into
|
||||||
* multiple cache files.
|
* multiple cache files. If set to {@link C#LENGTH_UNSET} then no fragmentation will occur.
|
||||||
|
* Using a small value allows for finer-grained cache eviction policies, at the cost of
|
||||||
|
* increased overhead both on the cache implementation and the file system. Values under
|
||||||
|
* {@code (2 * 1024 * 1024)} are not recommended.
|
||||||
* @param bufferSize The buffer size in bytes for writing to a cache file. A zero or negative
|
* @param bufferSize The buffer size in bytes for writing to a cache file. A zero or negative
|
||||||
* value disables buffering.
|
* value disables buffering.
|
||||||
*/
|
*/
|
||||||
public CacheDataSink(Cache cache, long maxCacheFileSize, int bufferSize) {
|
public CacheDataSink(Cache cache, long maxCacheFileSize, int bufferSize) {
|
||||||
|
Assertions.checkState(
|
||||||
|
maxCacheFileSize > 0 || maxCacheFileSize == C.LENGTH_UNSET,
|
||||||
|
"maxCacheFileSize must be positive or C.LENGTH_UNSET.");
|
||||||
|
if (maxCacheFileSize != C.LENGTH_UNSET
|
||||||
|
&& maxCacheFileSize < MIN_RECOMMENDED_MAX_CACHE_FILE_SIZE) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"maxCacheFileSize is below the minimum recommended value of "
|
||||||
|
+ MIN_RECOMMENDED_MAX_CACHE_FILE_SIZE
|
||||||
|
+ ". This may cause poor cache performance.");
|
||||||
|
}
|
||||||
this.cache = Assertions.checkNotNull(cache);
|
this.cache = Assertions.checkNotNull(cache);
|
||||||
this.maxCacheFileSize = maxCacheFileSize;
|
this.maxCacheFileSize = maxCacheFileSize == C.LENGTH_UNSET ? Long.MAX_VALUE : maxCacheFileSize;
|
||||||
this.bufferSize = bufferSize;
|
this.bufferSize = bufferSize;
|
||||||
syncFileDescriptor = true;
|
syncFileDescriptor = true;
|
||||||
}
|
}
|
||||||
|
@ -46,5 +46,4 @@ public final class CacheDataSinkFactory implements DataSink.Factory {
|
|||||||
public DataSink createDataSink() {
|
public DataSink createDataSink() {
|
||||||
return new CacheDataSink(cache, maxCacheFileSize, bufferSize);
|
return new CacheDataSink(cache, maxCacheFileSize, bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,14 +45,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public final class CacheDataSource implements DataSource {
|
public final class CacheDataSource implements DataSource {
|
||||||
|
|
||||||
/**
|
|
||||||
* Default maximum single cache file size.
|
|
||||||
*
|
|
||||||
* @see #CacheDataSource(Cache, DataSource, int)
|
|
||||||
* @see #CacheDataSource(Cache, DataSource, int, long)
|
|
||||||
*/
|
|
||||||
public static final long DEFAULT_MAX_CACHE_FILE_SIZE = 2 * 1024 * 1024;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags controlling the CacheDataSource's behavior. Possible flag values are {@link
|
* Flags controlling the CacheDataSource's behavior. Possible flag values are {@link
|
||||||
* #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} and {@link
|
* #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR} and {@link
|
||||||
@ -163,7 +155,7 @@ public final class CacheDataSource implements DataSource {
|
|||||||
* @param upstream A {@link DataSource} for reading data not in the cache.
|
* @param upstream A {@link DataSource} for reading data not in the cache.
|
||||||
*/
|
*/
|
||||||
public CacheDataSource(Cache cache, DataSource upstream) {
|
public CacheDataSource(Cache cache, DataSource upstream) {
|
||||||
this(cache, upstream, 0, DEFAULT_MAX_CACHE_FILE_SIZE);
|
this(cache, upstream, /* flags= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,29 +168,11 @@ public final class CacheDataSource implements DataSource {
|
|||||||
* and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0.
|
* and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0.
|
||||||
*/
|
*/
|
||||||
public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags) {
|
public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags) {
|
||||||
this(cache, upstream, flags, DEFAULT_MAX_CACHE_FILE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an instance with default {@link DataSource} and {@link DataSink} instances for
|
|
||||||
* reading and writing the cache. The sink is configured to fragment data such that no single
|
|
||||||
* cache file is greater than maxCacheFileSize bytes.
|
|
||||||
*
|
|
||||||
* @param cache The cache.
|
|
||||||
* @param upstream A {@link DataSource} for reading data not in the cache.
|
|
||||||
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
|
|
||||||
* and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0.
|
|
||||||
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the cached data size
|
|
||||||
* exceeds this value, then the data will be fragmented into multiple cache files. The
|
|
||||||
* finer-grained this is the finer-grained the eviction policy can be.
|
|
||||||
*/
|
|
||||||
public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags,
|
|
||||||
long maxCacheFileSize) {
|
|
||||||
this(
|
this(
|
||||||
cache,
|
cache,
|
||||||
upstream,
|
upstream,
|
||||||
new FileDataSource(),
|
new FileDataSource(),
|
||||||
new CacheDataSink(cache, maxCacheFileSize),
|
new CacheDataSink(cache, CacheDataSink.DEFAULT_MAX_CACHE_FILE_SIZE),
|
||||||
flags,
|
flags,
|
||||||
/* eventListener= */ null);
|
/* eventListener= */ null);
|
||||||
}
|
}
|
||||||
|
@ -46,20 +46,11 @@ public final class CacheDataSourceFactory implements DataSource.Factory {
|
|||||||
/** @see CacheDataSource#CacheDataSource(Cache, DataSource, int) */
|
/** @see CacheDataSource#CacheDataSource(Cache, DataSource, int) */
|
||||||
public CacheDataSourceFactory(
|
public CacheDataSourceFactory(
|
||||||
Cache cache, DataSource.Factory upstreamFactory, @CacheDataSource.Flags int flags) {
|
Cache cache, DataSource.Factory upstreamFactory, @CacheDataSource.Flags int flags) {
|
||||||
this(cache, upstreamFactory, flags, CacheDataSource.DEFAULT_MAX_CACHE_FILE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @see CacheDataSource#CacheDataSource(Cache, DataSource, int, long) */
|
|
||||||
public CacheDataSourceFactory(
|
|
||||||
Cache cache,
|
|
||||||
DataSource.Factory upstreamFactory,
|
|
||||||
@CacheDataSource.Flags int flags,
|
|
||||||
long maxCacheFileSize) {
|
|
||||||
this(
|
this(
|
||||||
cache,
|
cache,
|
||||||
upstreamFactory,
|
upstreamFactory,
|
||||||
new FileDataSourceFactory(),
|
new FileDataSourceFactory(),
|
||||||
new CacheDataSinkFactory(cache, maxCacheFileSize),
|
new CacheDataSinkFactory(cache, CacheDataSink.DEFAULT_MAX_CACHE_FILE_SIZE),
|
||||||
flags,
|
flags,
|
||||||
/* eventListener= */ null);
|
/* eventListener= */ null);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import com.google.android.exoplayer2.testutil.FakeDataSet;
|
|||||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
|
import com.google.android.exoplayer2.upstream.FileDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters;
|
import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
@ -341,7 +342,13 @@ public final class CacheUtilTest {
|
|||||||
cache,
|
cache,
|
||||||
/* cacheKeyFactory= */ null,
|
/* cacheKeyFactory= */ null,
|
||||||
// Set maxCacheFileSize to 10 to make sure there are multiple spans.
|
// Set maxCacheFileSize to 10 to make sure there are multiple spans.
|
||||||
new CacheDataSource(cache, dataSource, 0, 10),
|
new CacheDataSource(
|
||||||
|
cache,
|
||||||
|
dataSource,
|
||||||
|
new FileDataSource(),
|
||||||
|
new CacheDataSink(cache, /* maxCacheFileSize= */ 10),
|
||||||
|
/* flags= */ 0,
|
||||||
|
/* eventListener= */ null),
|
||||||
new byte[CacheUtil.DEFAULT_BUFFER_SIZE_BYTES],
|
new byte[CacheUtil.DEFAULT_BUFFER_SIZE_BYTES],
|
||||||
/* priorityTaskManager= */ null,
|
/* priorityTaskManager= */ null,
|
||||||
/* priority= */ 0,
|
/* priority= */ 0,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user