Skip to content

Commit

Permalink
Merge pull request #2 from dnnyg33/master
Browse files Browse the repository at this point in the history
Suspend functions and Custom json mappers
  • Loading branch information
diefferson authored May 22, 2020
2 parents 55846eb + c7bb1c7 commit c61c60e
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 127 deletions.
10 changes: 0 additions & 10 deletions .idea/codeStyles/Project.xml

This file was deleted.

19 changes: 0 additions & 19 deletions .idea/gradle.xml

This file was deleted.

44 changes: 0 additions & 44 deletions .idea/misc.xml

This file was deleted.

17 changes: 0 additions & 17 deletions app/src/test/java/io/coroutines/cache/ExampleUnitTest.kt

This file was deleted.

Empty file modified gradlew
100644 → 100755
Empty file.
2 changes: 2 additions & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation "com.squareup.retrofit2:retrofit:2.9.0"


}
112 changes: 75 additions & 37 deletions library/src/main/java/io/coroutines/cache/core/CoroutinesCache.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
package io.coroutines.cache.core

import android.content.Context
import android.util.Log
import androidx.lifecycle.*
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import io.coroutines.cache.dao.Cache
import io.coroutines.cache.dao.RealmDatabase
import kotlinx.coroutines.*
import java.lang.IllegalStateException
import java.util.*
import kotlin.collections.HashMap
import kotlin.coroutines.CoroutineContext

open class CoroutinesCache(private var context: Context, @PublishedApi internal val test: Boolean = false, @PublishedApi internal val lifecycleOwner: LifecycleOwner? = null):CoroutineScope{
open class CoroutinesCache(
private var context: Context,
@PublishedApi internal val byPassCache: Boolean = false,
@PublishedApi internal val lifecycleOwner: LifecycleOwner? = null,
@PublishedApi internal val jsonMapper: JsonMapper
) : CoroutineScope {

private val executionJob: Job by lazy { Job() }
private val executionJob: Job by lazy { Job() }

override val coroutineContext: CoroutineContext by lazy {
Dispatchers.Default + executionJob
}

@PublishedApi internal val database:RealmDatabase by lazy {
@PublishedApi
internal val database: RealmDatabase by lazy {
RealmDatabase(context).apply {
initDatabase()
}
}

init {
lifecycleOwner?.lifecycle?.addObserver(object:LifecycleObserver {
lifecycleOwner?.lifecycle?.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
database.deleteLifecycle()
Expand All @@ -40,85 +43,120 @@ open class CoroutinesCache(private var context: Context, @PublishedApi internal
database.clear()
}

fun deleteItem(key:String){
fun deleteItem(key: String) {
database.deleteItem(key)
}


inline fun <reified T:Any> asyncCache(noinline source: suspend ()->Deferred<T>, key: String, cachePolicy: CachePolicy): Deferred<T> {
return if(test){
async{source().await()}
}else{
when(cachePolicy){
is CachePolicy.TimeCache-> cacheTimeResolver( source, key, cachePolicy)
suspend inline fun <reified T : Any> asyncCache(
noinline source: suspend () -> T,
key: String,
cachePolicy: CachePolicy
): T {
return if (byPassCache) {
return source()
} else {
when (cachePolicy) {
is CachePolicy.TimeCache -> cacheTimeResolver(source, key, cachePolicy)
is CachePolicy.EvictProvider -> cacheProviderResolver(source, key, cachePolicy)
is CachePolicy.LifecycleCache -> cacheLifecycleResolver(source, key)
}
}
}

@PublishedApi internal inline fun <reified T : Any> cacheTimeResolver(noinline source: suspend () -> Deferred<T>, key: String, cachePolicy: CachePolicy.TimeCache): Deferred<T> {
@PublishedApi
internal suspend inline fun <reified T : Any> cacheTimeResolver(
noinline source: suspend () -> T,
key: String,
cachePolicy: CachePolicy.TimeCache
): T {
getFromCacheValidateTime<T>(key, cachePolicy)?.let {
return async { it }
return it
} ?: run {
return getFromSource(source, key)
}
}

@PublishedApi internal inline fun <reified T : Any> cacheProviderResolver(noinline source: suspend () -> Deferred<T>, key: String, cachePolicy: CachePolicy.EvictProvider): Deferred<T> {
@PublishedApi
internal suspend inline fun <reified T : Any> cacheProviderResolver(
noinline source: suspend () -> T,
key: String,
cachePolicy: CachePolicy.EvictProvider
): T {
return if (cachePolicy.fromSource) {
getFromSource(source, key)
} else {
getFromCache<T>(key)?.let {
return async { it }
return it
} ?: run {
return getFromSource(source, key)
}
}
}

@PublishedApi internal inline fun <reified T : Any> cacheLifecycleResolver(noinline source: suspend () -> Deferred<T>, key: String): Deferred<T> {
if(lifecycleOwner == null){
@PublishedApi
internal suspend inline fun <reified T : Any> cacheLifecycleResolver(
noinline source: suspend () -> T,
key: String
): T {
if (lifecycleOwner == null) {
throw IllegalStateException("Necessary pass a lifecycleowner to Coroutines Cache Constructor")
}

return getFromCache<T>(key)?.let {
async { it }
}?:run{
return getFromCache<T>(key) ?: run {
getFromSource(source, key, true)
}
}

@PublishedApi internal fun <T> getFromSource(source: suspend ()->Deferred<T>, key: String, isLifecycle: Boolean = false):Deferred<T>{
return async {
val result = source().await()
@PublishedApi
internal suspend fun <T> getFromSource(
source: suspend () -> T,
key: String,
isLifecycle: Boolean = false
): T {
val result = source()
if (jsonMapper.assertValidJson(result)) {
database.getDatabase().apply {
beginTransaction()
copyToRealmOrUpdate(Cache(key, Gson().toJson(result), Calendar.getInstance().time, isLifecycle))
copyToRealmOrUpdate(
Cache(
key,
jsonMapper.toJson(result!!),
Calendar.getInstance().time,
isLifecycle
)
)
commitTransaction()
close()

}
result
}
return result
}

@PublishedApi internal inline fun <reified T:Any> getFromCache(key: String): T? {
val resultDb = database.getDatabase().where(Cache::class.java).equalTo("id", key).findFirst()?.data
@PublishedApi
internal inline fun <reified T : Any> getFromCache(key: String): T? {
val resultDb =
database.getDatabase().where(Cache::class.java).equalTo("id", key).findFirst()?.data
return if (resultDb != null) {
val listType = object : TypeToken<T>() {}.type
Gson().fromJson(resultDb, listType) as T
jsonMapper.fromJson(resultDb, listType) as T?
} else {
return null
}
}

@PublishedApi internal inline fun <reified T:Any> getFromCacheValidateTime(key: String, timeCache: CachePolicy.TimeCache): T? {
val resultDb = database.getDatabase().where(Cache::class.java).equalTo("id", key).findFirst()
@PublishedApi
internal inline fun <reified T : Any> getFromCacheValidateTime(
key: String,
timeCache: CachePolicy.TimeCache
): T? {
val resultDb =
database.getDatabase().where(Cache::class.java).equalTo("id", key).findFirst()
return if (resultDb != null) {
if(resultDb.date.isValidCache(timeCache.duration, timeCache.timeUnit)){
if (resultDb.date.isValidCache(timeCache.duration, timeCache.timeUnit)) {
val listType = object : TypeToken<T>() {}.type
Gson().fromJson(resultDb.data, listType) as T
}else{
jsonMapper.fromJson(resultDb.data, listType) as T?
} else {
null
}
} else {
Expand Down
40 changes: 40 additions & 0 deletions library/src/main/java/io/coroutines/cache/core/JsonMapper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.coroutines.cache.core

import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import retrofit2.Response
import java.io.StringReader
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

interface JsonMapper {
fun toJson(src: Any): String
fun <T> fromJson(json: String, typeOfT: Type): T
fun <T> assertValidJson(result: T): Boolean
}

class GsonRetrofitMapper : JsonMapper {
override fun toJson(src: Any): String {
val response = src as Response<*>
val body = response.body()!!
return Gson().toJson(body, body.javaClass)
}

@Throws(JsonSyntaxException::class)
override fun <T> fromJson(json: String, retrofitResponse: Type): T {
val reader = StringReader(json)
val parameterizedType = retrofitResponse as ParameterizedType
val storedType = parameterizedType.actualTypeArguments[0]
val storedJson = Gson().fromJson(reader, storedType) as Any
return Response.success(storedJson) as T
}

override fun <T> assertValidJson(result: T): Boolean {
if (result is Response<*>) {
if (result.isSuccessful) {
return true
}
}
return false
}
}

0 comments on commit c61c60e

Please sign in to comment.