Author: Adam Pioterek <adam.pioterek@protonmail.ch>
better caching
app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt | 38 ++++++-- app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt | 36 +++----
diff --git a/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt b/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt index 15832219c47408de967b6edf2b94b02992de65ae..a4271b84969e54773e0debddae5e6354dd73bb02 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/CacheManager.kt @@ -7,13 +7,15 @@ class CacheManager private constructor(context: Context) { companion object { private var manager: CacheManager? = null - fun getCacheManager(context: Context) : CacheManager { + fun getCacheManager(context: Context): CacheManager { return if (manager == null) { manager = CacheManager(context) manager!! } else manager!! } + + val MAX_SIZE = 40 } private var cachePreferences: SharedPreferences = context.getSharedPreferences("ml.adamsprogs.bimba.cachePreferences.cache", Context.MODE_PRIVATE) @@ -22,6 +24,10 @@ private var cache: HashMap<String, Plate> = HashMap() private var cacheHits: HashMap<String, Int> = HashMap() + fun keys(): List<Plate> { + return cache.map { Plate(it.key.split("@")[0], it.key.split("@")[1], null) } + } + fun hasAll(plates: HashSet<Plate>): Boolean { plates .filterNot { has(it) } @@ -37,31 +43,40 @@ return false } fun has(plate: Plate): Boolean { - val key = "${plate.line}@${plate.stop}" - return cache.containsKey(key) + return cache.containsKey(key(plate)) } fun push(plates: HashSet<Plate>) { + val removeNumber = cache.size + plates.size - MAX_SIZE val editor = cachePreferences.edit() + val editorCacheHits = cacheHitsPreferences.edit() + cacheHits.map { "${it.value}|${it.key}" }.sortedBy { it }.slice(0 until removeNumber).forEach { + val key = it.split("|")[1] + cache.remove(key) + editor.remove(key) + } for (plate in plates) { - val key = "${plate.line}@${plate.stop}" + val key = key(plate) cache[key] = plate + cacheHits[key] = 0 editor.putString(key, cache[key].toString()) + editorCacheHits.putInt(key, 0) } editor.apply() + editorCacheHits.apply() } fun push(plate: Plate) { val editorCache = cachePreferences.edit() val editorCacheHits = cacheHitsPreferences.edit() - if (cacheHits.size == 40) { //todo size? + if (cacheHits.size == MAX_SIZE) { val key = cacheHits.minBy { it.value }?.key cache.remove(key) editorCache.remove(key) cacheHits.remove(key) editorCacheHits.remove(key) } - val key = "${plate.line}@${plate.stop}" + val key = key(plate) cache[key] = plate cacheHits[key] = 0 editorCache.putString(key, plate.toString()) @@ -77,7 +92,7 @@ val value = get(plate) if (value == null) result.add(plate) else - result.add(get(plate)!!) + result.add(value) } return result } @@ -85,19 +100,20 @@ fun get(plate: Plate): Plate? { if (!has(plate)) return null - val key = "${plate.line}@${plate.stop}" + val key = key(plate) val hits = cacheHits[key] if (hits != null) cacheHits[key] = hits + 1 return cache[key] } - fun recreate() { - TODO() + fun recreate(stopDeparturesByPlates: HashSet<Plate>) { + stopDeparturesByPlates.forEach { cache[key(it)] = it } } init { cache = cacheFromString(cachePreferences.all) + @Suppress("UNCHECKED_CAST") cacheHits = cacheHitsPreferences.all as HashMap<String, Int> } @@ -108,4 +124,6 @@ result[key] = Plate.fromString(value as String) } return result } + + private fun key(plate: Plate) = "${plate.line}@${plate.stop}" } \ No newline at end of file diff --git a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt index 2ca7bd58bf3a7055d735292963e1015965874a92..8c5b119273aedac36074beeadf1a4d0ec471328f 100644 --- a/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt +++ b/app/src/main/java/ml/adamsprogs/bimba/models/Timetable.kt @@ -5,7 +5,6 @@ import android.database.CursorIndexOutOfBoundsException import android.database.sqlite.SQLiteCantOpenDatabaseException import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabaseCorruptException -import android.util.Log import ml.adamsprogs.bimba.CacheManager import java.io.File @@ -61,7 +60,7 @@ throw SQLiteCantOpenDatabaseException("db corrupt") } this.db = db - //todo cacheManager.recreate() + cacheManager.recreate(getStopDeparturesByPlates(cacheManager.keys().toSet() as HashSet<Plate>)) //todo optimise } fun getStops(): ArrayList<StopSuggestion> { @@ -108,28 +107,28 @@ } fun getStopDepartures(stopId: String, lineId: String? = null): HashMap<String, ArrayList<Departure>> { val plates = HashSet<Plate>() + val toGet = HashSet<Plate>() if (lineId == null) { - for (line in getLinesForStop(stopId)) { - val plate = Plate(line, stopId, null) - if (cacheManager.has(plate)) - plates.add(cacheManager.get(plate)!!) - else { - val p = Plate(line, stopId, getStopDeparturesByLine(line, stopId)) //fixme to one query - plates.add(p) - cacheManager.push(p) - } - } + getLinesForStop(stopId) + .map { Plate(it, stopId, null) } + .forEach { + if (cacheManager.has(it)) + plates.add(cacheManager.get(it)!!) + else { + toGet.add(it) + } + } } else { val plate = Plate(lineId, stopId, null) if (cacheManager.has(plate)) plates.add(cacheManager.get(plate)!!) else { - val p = Plate(lineId, stopId, getStopDeparturesByLine(lineId, stopId)) - plates.add(p) - cacheManager.push(p) + toGet.add(plate) } } + + getStopDeparturesByPlates(toGet).forEach {cacheManager.push(it); plates.add(it)} return Plate.join(plates) } @@ -145,7 +144,7 @@ else toGet.add(plate) } - result.addAll(getStopDeparturesByPlates(toGet)) + getStopDeparturesByPlates(toGet).forEach {cacheManager.push(it); result.add(it)} return Plate.join(result) } @@ -196,11 +195,6 @@ return lines } fun getFavouriteElement(plate: Plate): String { - val q = "select name || ' (' || stops.symbol || stops.number || '): \n' " + - "|| lines.number || ' → ' || headsign from timetables join stops on (stops.id = stop_id) " + - "join lines on(lines.id = line_id) join nodes on(nodes.symbol = stops.symbol) where " + - "stop_id = ${plate.stop} and line_id = ${plate.line}" - val cursor = db.rawQuery("select name || ' (' || stops.symbol || stops.number || '): \n' " + "|| lines.number || ' → ' || headsign from timetables join stops on (stops.id = stop_id) " + "join lines on(lines.id = line_id) join nodes on(nodes.symbol = stops.symbol) where " +