-
-
Save programmerr47/8c138b1974092b0bda76a36a0e26bc97 to your computer and use it in GitHub Desktop.
Simple implementation for CacheProvider interface
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface TimeProvider { | |
val currentMs: Long | |
} | |
object SystemTimeProvider : TimeProvider { | |
override val currentMs: Long get() = System.currentTimeMillis() | |
} | |
interface CacheProvider { | |
fun obtainFresh(key: String): String? | |
fun obtain(key: String): String? | |
fun store(key: String, value: String, stalesAfterMs: Long, expiresAfterMs: Long) | |
} | |
class SqliteCacheProvider(context: Context, name: String, version: Int, private val timeProvider: TimeProvider = SystemTimeProvider) : | |
SQLiteOpenHelper(context, name, null, version), CacheProvider { | |
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { | |
db.execSQL("DROP TABLE IF EXISTS $T_CACHE") | |
onCreate(db) | |
} | |
override fun onCreate(db: SQLiteDatabase) { | |
db.execSQL("CREATE TABLE $T_CACHE ($C_KEY TEXT PRIMARY KEY, $C_VALUE TEXT, $C_STALES INTEGER, $C_EXPIRES INTEGER);") | |
} | |
override fun obtainFresh(key: String) = | |
obtainBy("SELECT $C_VALUE FROM $T_CACHE WHERE $C_KEY = ? AND $C_STALES > ${timeProvider.currentMs} LIMIT 1", arrayOf(key)) | |
override fun obtain(key: String) = | |
obtainBy("SELECT $C_VALUE FROM $T_CACHE WHERE $C_KEY = ? AND $C_EXPIRES > ${timeProvider.currentMs} LIMIT 1", arrayOf(key)) | |
private fun obtainBy(query: String, args: Array<String>): String? { | |
return try { | |
readableDatabase.rawQuery(query, args).use { | |
if (it.count == 0) null | |
else { | |
it.moveToFirst() | |
it.getString(C_VALUE) | |
} | |
} | |
} catch (e: SQLiteException) { | |
null | |
} | |
} | |
override fun store(key: String, value: String, stalesAfterMs: Long, expiresAfterMs: Long) { | |
val currentTime = timeProvider.currentMs | |
val values = ContentValues(4).also { | |
it[C_KEY] = key | |
it[C_VALUE] = value | |
it[C_STALES] = correctTime(currentTime, stalesAfterMs) | |
it[C_EXPIRES] = correctTime(currentTime, expiresAfterMs) | |
} | |
writableDatabase.insertWithOnConflict(T_CACHE, "", values, SQLiteDatabase.CONFLICT_REPLACE) | |
} | |
fun clean() = writableDatabase.execSQL("DELETE FROM $T_CACHE WHERE $C_EXPIRES < ${timeProvider.currentMs}") | |
fun clear() = writableDatabase.execSQL("DELETE FROM $T_CACHE") | |
private fun correctTime(time: Long, addend: Long) = (time + addend).let { | |
if (it < time) Long.MAX_VALUE | |
else it | |
} | |
companion object { | |
private const val T_CACHE = "cache" | |
private const val C_KEY = "c_key" | |
private const val C_VALUE = "c_value" | |
private const val C_STALES = "c_stales" | |
private const val C_EXPIRES = "c_expires" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment