mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Implement CacheFileMetadataIndex using SQLite
PiperOrigin-RevId: 232670039
This commit is contained in:
parent
7eed60c9e3
commit
a6c1dbe156
5 changed files with 137 additions and 24 deletions
|
|
@ -35,8 +35,10 @@ public final class VersionTable {
|
||||||
public static final int VERSION_UNSET = -1;
|
public static final int VERSION_UNSET = -1;
|
||||||
/** Version of tables used for offline functionality. */
|
/** Version of tables used for offline functionality. */
|
||||||
public static final int FEATURE_OFFLINE = 0;
|
public static final int FEATURE_OFFLINE = 0;
|
||||||
/** Version of tables used for cache functionality. */
|
/** Version of tables used for cache content metadata. */
|
||||||
public static final int FEATURE_CACHE = 1;
|
public static final int FEATURE_CACHE_CONTENT_METADATA = 1;
|
||||||
|
/** Version of tables used for cache file metadata. */
|
||||||
|
public static final int FEATURE_CACHE_FILE_METADATA = 2;
|
||||||
|
|
||||||
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Versions";
|
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Versions";
|
||||||
|
|
||||||
|
|
@ -54,7 +56,7 @@ public final class VersionTable {
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@IntDef({FEATURE_OFFLINE, FEATURE_CACHE})
|
@IntDef({FEATURE_OFFLINE, FEATURE_CACHE_CONTENT_METADATA, FEATURE_CACHE_FILE_METADATA})
|
||||||
private @interface Feature {}
|
private @interface Feature {}
|
||||||
|
|
||||||
private VersionTable() {}
|
private VersionTable() {}
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,72 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.upstream.cache;
|
package com.google.android.exoplayer2.upstream.cache;
|
||||||
|
|
||||||
import java.util.Collections;
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import com.google.android.exoplayer2.database.DatabaseProvider;
|
||||||
|
import com.google.android.exoplayer2.database.VersionTable;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/** Maintains an index of cache file metadata. */
|
/** Maintains an index of cache file metadata. */
|
||||||
/* package */ class CacheFileMetadataIndex {
|
/* package */ final class CacheFileMetadataIndex {
|
||||||
|
|
||||||
|
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "CacheFileMetadata";
|
||||||
|
private static final int TABLE_VERSION = 1;
|
||||||
|
|
||||||
|
private static final String COLUMN_NAME = "name";
|
||||||
|
private static final String COLUMN_LENGTH = "length";
|
||||||
|
private static final String COLUMN_LAST_ACCESS_TIMESTAMP = "last_access_timestamp";
|
||||||
|
|
||||||
|
private static final int COLUMN_INDEX_NAME = 0;
|
||||||
|
private static final int COLUMN_INDEX_LENGTH = 1;
|
||||||
|
private static final int COLUMN_INDEX_LAST_ACCESS_TIMESTAMP = 2;
|
||||||
|
|
||||||
|
private static final String WHERE_NAME_EQUALS = COLUMN_INDEX_NAME + " = ?";
|
||||||
|
|
||||||
|
private static final String[] COLUMNS =
|
||||||
|
new String[] {
|
||||||
|
COLUMN_NAME, COLUMN_LENGTH, COLUMN_LAST_ACCESS_TIMESTAMP,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String SQL_DROP_TABLE_IF_EXISTS = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||||
|
private static final String SQL_CREATE_TABLE =
|
||||||
|
"CREATE TABLE "
|
||||||
|
+ TABLE_NAME
|
||||||
|
+ " ("
|
||||||
|
+ COLUMN_NAME
|
||||||
|
+ " TEXT PRIMARY KEY NOT NULL,"
|
||||||
|
+ COLUMN_LENGTH
|
||||||
|
+ " INTEGER NOT NULL,"
|
||||||
|
+ COLUMN_LAST_ACCESS_TIMESTAMP
|
||||||
|
+ " INTEGER NOT NULL)";
|
||||||
|
|
||||||
|
private final DatabaseProvider databaseProvider;
|
||||||
|
|
||||||
|
private boolean initialized;
|
||||||
|
|
||||||
|
public CacheFileMetadataIndex(DatabaseProvider databaseProvider) {
|
||||||
|
this.databaseProvider = databaseProvider;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all file metadata keyed by file name. The returned map is mutable and may be modified
|
* Returns all file metadata keyed by file name. The returned map is mutable and may be modified
|
||||||
* by the caller.
|
* by the caller.
|
||||||
*/
|
*/
|
||||||
public Map<String, CacheFileMetadata> getAll() {
|
public Map<String, CacheFileMetadata> getAll() {
|
||||||
return Collections.emptyMap();
|
ensureInitialized();
|
||||||
|
try (Cursor cursor = getCursor()) {
|
||||||
|
Map<String, CacheFileMetadata> fileMetadata = new HashMap<>(cursor.getCount());
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String name = cursor.getString(COLUMN_INDEX_NAME);
|
||||||
|
long length = cursor.getLong(COLUMN_INDEX_LENGTH);
|
||||||
|
long lastAccessTimestamp = cursor.getLong(COLUMN_INDEX_LAST_ACCESS_TIMESTAMP);
|
||||||
|
fileMetadata.put(name, new CacheFileMetadata(length, lastAccessTimestamp));
|
||||||
|
}
|
||||||
|
return fileMetadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,11 +89,15 @@ import java.util.Set;
|
||||||
* @param name The name of the file.
|
* @param name The name of the file.
|
||||||
* @param length The file length.
|
* @param length The file length.
|
||||||
* @param lastAccessTimestamp The file last access timestamp.
|
* @param lastAccessTimestamp The file last access timestamp.
|
||||||
* @return Whether the index was updated successfully.
|
|
||||||
*/
|
*/
|
||||||
public boolean set(String name, long length, long lastAccessTimestamp) {
|
public void set(String name, long length, long lastAccessTimestamp) {
|
||||||
// TODO.
|
ensureInitialized();
|
||||||
return false;
|
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(COLUMN_NAME, name);
|
||||||
|
values.put(COLUMN_LENGTH, length);
|
||||||
|
values.put(COLUMN_LAST_ACCESS_TIMESTAMP, lastAccessTimestamp);
|
||||||
|
writableDatabase.replace(TABLE_NAME, /* nullColumnHack= */ null, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -49,7 +106,9 @@ import java.util.Set;
|
||||||
* @param name The name of the file whose metadata is to be removed.
|
* @param name The name of the file whose metadata is to be removed.
|
||||||
*/
|
*/
|
||||||
public void remove(String name) {
|
public void remove(String name) {
|
||||||
// TODO.
|
ensureInitialized();
|
||||||
|
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||||
|
writableDatabase.delete(TABLE_NAME, WHERE_NAME_EQUALS, new String[] {name});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -58,6 +117,55 @@ import java.util.Set;
|
||||||
* @param names The names of the files whose metadata is to be removed.
|
* @param names The names of the files whose metadata is to be removed.
|
||||||
*/
|
*/
|
||||||
public void removeAll(Set<String> names) {
|
public void removeAll(Set<String> names) {
|
||||||
// TODO.
|
ensureInitialized();
|
||||||
|
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||||
|
writableDatabase.beginTransaction();
|
||||||
|
try {
|
||||||
|
for (String name : names) {
|
||||||
|
writableDatabase.delete(TABLE_NAME, WHERE_NAME_EQUALS, new String[] {name});
|
||||||
|
}
|
||||||
|
writableDatabase.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
writableDatabase.endTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureInitialized() {
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase();
|
||||||
|
int version =
|
||||||
|
VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_CACHE_FILE_METADATA);
|
||||||
|
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
||||||
|
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||||
|
writableDatabase.beginTransaction();
|
||||||
|
try {
|
||||||
|
VersionTable.setVersion(
|
||||||
|
writableDatabase, VersionTable.FEATURE_CACHE_FILE_METADATA, TABLE_VERSION);
|
||||||
|
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
||||||
|
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
||||||
|
writableDatabase.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
writableDatabase.endTransaction();
|
||||||
|
}
|
||||||
|
} else if (version < TABLE_VERSION) {
|
||||||
|
// There is no previous version currently.
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cursor getCursor() {
|
||||||
|
return databaseProvider
|
||||||
|
.getReadableDatabase()
|
||||||
|
.query(
|
||||||
|
TABLE_NAME,
|
||||||
|
COLUMNS,
|
||||||
|
/* selection */ null,
|
||||||
|
/* selectionArgs= */ null,
|
||||||
|
/* groupBy= */ null,
|
||||||
|
/* having= */ null,
|
||||||
|
/* orderBy= */ null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -678,7 +678,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
// the entire table. Currently this implementation only encrypts new and updated entries.
|
// the entire table. Currently this implementation only encrypts new and updated entries.
|
||||||
private static final class SQLiteStorage implements Storage {
|
private static final class SQLiteStorage implements Storage {
|
||||||
|
|
||||||
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Cache";
|
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "CacheContentMetadata";
|
||||||
private static final int TABLE_VERSION = 1;
|
private static final int TABLE_VERSION = 1;
|
||||||
|
|
||||||
private static final String COLUMN_ID = "id";
|
private static final String COLUMN_ID = "id";
|
||||||
|
|
@ -736,7 +736,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
public boolean exists() {
|
public boolean exists() {
|
||||||
return file.exists()
|
return file.exists()
|
||||||
&& VersionTable.getVersion(
|
&& VersionTable.getVersion(
|
||||||
databaseProvider.getReadableDatabase(), VersionTable.FEATURE_CACHE)
|
databaseProvider.getReadableDatabase(),
|
||||||
|
VersionTable.FEATURE_CACHE_CONTENT_METADATA)
|
||||||
!= VersionTable.VERSION_UNSET;
|
!= VersionTable.VERSION_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -755,7 +756,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
try {
|
try {
|
||||||
int version =
|
int version =
|
||||||
VersionTable.getVersion(
|
VersionTable.getVersion(
|
||||||
databaseProvider.getReadableDatabase(), VersionTable.FEATURE_CACHE);
|
databaseProvider.getReadableDatabase(),
|
||||||
|
VersionTable.FEATURE_CACHE_CONTENT_METADATA);
|
||||||
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
||||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||||
writableDatabase.beginTransaction();
|
writableDatabase.beginTransaction();
|
||||||
|
|
@ -870,7 +872,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeTable(SQLiteDatabase writableDatabase) {
|
private void initializeTable(SQLiteDatabase writableDatabase) {
|
||||||
VersionTable.setVersion(writableDatabase, VersionTable.FEATURE_CACHE, TABLE_VERSION);
|
VersionTable.setVersion(
|
||||||
|
writableDatabase, VersionTable.FEATURE_CACHE_CONTENT_METADATA, TABLE_VERSION);
|
||||||
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
||||||
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -253,13 +253,12 @@ public final class SimpleCache implements Cache {
|
||||||
String fileName = span.file.getName();
|
String fileName = span.file.getName();
|
||||||
long length = span.length;
|
long length = span.length;
|
||||||
long lastAccessTimestamp = System.currentTimeMillis();
|
long lastAccessTimestamp = System.currentTimeMillis();
|
||||||
// Updating the file itself to incorporate the new last access timestamp is much slower than
|
boolean updateFile = false;
|
||||||
// updating the file index. Hence we only update the file if we don't have a file index, or if
|
|
||||||
// updating the file index failed.
|
|
||||||
boolean updateFile;
|
|
||||||
if (fileIndex != null) {
|
if (fileIndex != null) {
|
||||||
updateFile = !fileIndex.set(fileName, length, lastAccessTimestamp);
|
fileIndex.set(fileName, length, lastAccessTimestamp);
|
||||||
} else {
|
} else {
|
||||||
|
// Updating the file itself to incorporate the new last access timestamp is much slower than
|
||||||
|
// updating the file index. Hence we only update the file if we don't have a file index.
|
||||||
updateFile = true;
|
updateFile = true;
|
||||||
}
|
}
|
||||||
SimpleCacheSpan newSpan =
|
SimpleCacheSpan newSpan =
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.database;
|
package com.google.android.exoplayer2.database;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.database.VersionTable.FEATURE_CACHE;
|
import static com.google.android.exoplayer2.database.VersionTable.FEATURE_CACHE_CONTENT_METADATA;
|
||||||
import static com.google.android.exoplayer2.database.VersionTable.FEATURE_OFFLINE;
|
import static com.google.android.exoplayer2.database.VersionTable.FEATURE_OFFLINE;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
|
@ -61,8 +61,9 @@ public class VersionTableTest {
|
||||||
VersionTable.setVersion(writableDatabase, FEATURE_OFFLINE, 10);
|
VersionTable.setVersion(writableDatabase, FEATURE_OFFLINE, 10);
|
||||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(10);
|
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(10);
|
||||||
|
|
||||||
VersionTable.setVersion(writableDatabase, FEATURE_CACHE, 5);
|
VersionTable.setVersion(writableDatabase, FEATURE_CACHE_CONTENT_METADATA, 5);
|
||||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_CACHE)).isEqualTo(5);
|
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_CACHE_CONTENT_METADATA))
|
||||||
|
.isEqualTo(5);
|
||||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(10);
|
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue