diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 34dc27c..1bec35e 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,31 +3,6 @@ - - - - - - - - - - diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index cc04cd3..51fa3e5 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -35,7 +35,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 03c244f..315a5aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: "kotlin-kapt" android { compileSdkVersion 28 @@ -22,10 +23,15 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "androidx.appcompat:appcompat:1.1.0-alpha05" + implementation "androidx.constraintlayout:constraintlayout:1.1.3" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1" + implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.26.1-eap13" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.26.1-eap13" + kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" implementation project(":library") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cd0e351..22dc9aa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,19 +1,21 @@ + package="io.coroutines.cache"> + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + + - + - + diff --git a/app/src/main/java/io/coroutines/cache/Main2Activity.kt b/app/src/main/java/io/coroutines/cache/Main2Activity.kt new file mode 100644 index 0000000..8fcd80f --- /dev/null +++ b/app/src/main/java/io/coroutines/cache/Main2Activity.kt @@ -0,0 +1,12 @@ +package io.coroutines.cache + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity + +class Main2Activity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main2) + } +} diff --git a/app/src/main/java/io/coroutines/cache/MainActivity.kt b/app/src/main/java/io/coroutines/cache/MainActivity.kt index c27f496..1be31af 100644 --- a/app/src/main/java/io/coroutines/cache/MainActivity.kt +++ b/app/src/main/java/io/coroutines/cache/MainActivity.kt @@ -1,13 +1,12 @@ package io.coroutines.cache +import android.content.Intent import android.os.Bundle -import android.support.v7.app.AppCompatActivity +import androidx.appcompat.app.AppCompatActivity import io.coroutines.cache.core.CachePolicy import io.coroutines.cache.core.CoroutinesCache import kotlinx.android.synthetic.main.activity_main.* import kotlinx.coroutines.* -import kotlinx.coroutines.android.Main -import java.util.concurrent.TimeUnit import kotlin.coroutines.CoroutineContext class MainActivity : AppCompatActivity(), CoroutineScope { @@ -25,13 +24,18 @@ class MainActivity : AppCompatActivity(), CoroutineScope { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + launch { delay(3000) - val myAsyncTet = coroutinesCache.asyncCache({ async { "My Async text!" } }, "Key", CachePolicy.LifeCache(10, TimeUnit.SECONDS)).await() + val myAsyncTet = coroutinesCache.asyncCache({ async { "My Async text!" } }, "Key",CachePolicy.LifecycleCache).await() withContext(Dispatchers.Main){ text.text = myAsyncTet } } + + text.setOnClickListener { + startActivity(Intent(this, Main2Activity::class.java)) + } } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3dfda5d..4d6fffa 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main2.xml b/app/src/main/res/layout/activity_main2.xml new file mode 100644 index 0000000..a26e04b --- /dev/null +++ b/app/src/main/res/layout/activity_main2.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 114c648..5e69e3b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.0' + ext.kotlin_version = '1.3.21' + ext.lifecycle_version = "2.0.0" repositories { maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.4.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:5.0.0" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle.properties b/gradle.properties index 85be9ea..20032b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,3 +13,5 @@ org.gradle.jvmargs=-Xmx1536m # org.gradle.parallel=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official +android.enableJetifier=true +android.useAndroidX=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9a4163a..0f980f0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue May 14 10:44:00 BRT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/library/build.gradle b/library/build.gradle index 3c5420b..a7ea008 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'realm-android' group = 'com.github.diefferson' -version='0.2.2' +version='0.2.3' android { compileSdkVersion 28 @@ -29,6 +29,10 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.26.1-eap13" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1" implementation 'com.google.code.gson:gson:2.8.5' + implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" + kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" + } \ No newline at end of file diff --git a/library/src/main/java/io/coroutines/cache/core/CachePolicy.kt b/library/src/main/java/io/coroutines/cache/core/CachePolicy.kt index 8ddcca4..af57343 100644 --- a/library/src/main/java/io/coroutines/cache/core/CachePolicy.kt +++ b/library/src/main/java/io/coroutines/cache/core/CachePolicy.kt @@ -4,5 +4,6 @@ import java.util.concurrent.TimeUnit sealed class CachePolicy{ data class EvictProvider(val fromSource:Boolean) :CachePolicy() - data class LifeCache(val duration: Int, val timeUnit: TimeUnit):CachePolicy() + data class TimeCache(val duration: Int, val timeUnit: TimeUnit):CachePolicy() + object LifecycleCache:CachePolicy() } \ No newline at end of file diff --git a/library/src/main/java/io/coroutines/cache/core/CoroutinesCache.kt b/library/src/main/java/io/coroutines/cache/core/CoroutinesCache.kt index dc487c1..7ff8cc9 100644 --- a/library/src/main/java/io/coroutines/cache/core/CoroutinesCache.kt +++ b/library/src/main/java/io/coroutines/cache/core/CoroutinesCache.kt @@ -1,15 +1,19 @@ 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, val test: Boolean = false):CoroutineScope{ +open class CoroutinesCache(private var context: Context, val test: Boolean = false, val lifecycleOwner: LifecycleOwner? = null):CoroutineScope{ private val executionJob: Job by lazy { Job() } @@ -23,19 +27,37 @@ open class CoroutinesCache(private var context: Context, val test: Boolean = fal } } + init { + lifecycleOwner?.lifecycle?.addObserver(object:LifecycleObserver { + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun destroy() { + database.deleteLifecycle() + } + }) + } + + fun clear() { + database.clear() + } + + fun deleteItem(key:String){ + database.deleteItem(key) + } + + inline fun asyncCache(noinline source: suspend ()->Deferred, key: String, cachePolicy: CachePolicy): Deferred { return if(test){ async{source().await()} }else{ when(cachePolicy){ - - is CachePolicy.LifeCache-> cacheLifeResolver( source, key, 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 cacheLifeResolver(noinline source: suspend () -> Deferred, key: String, cachePolicy: CachePolicy.LifeCache): Deferred { + @PublishedApi internal inline fun cacheTimeResolver(noinline source: suspend () -> Deferred, key: String, cachePolicy: CachePolicy.TimeCache): Deferred { getFromCacheValidateTime(key, cachePolicy)?.let { return async { it } } ?: run { @@ -55,12 +77,24 @@ open class CoroutinesCache(private var context: Context, val test: Boolean = fal } } - @PublishedApi internal fun getFromSource(source: suspend ()->Deferred, key: String):Deferred{ + @PublishedApi internal inline fun cacheLifecycleResolver(noinline source: suspend () -> Deferred, key: String): Deferred { + if(lifecycleOwner == null){ + throw IllegalStateException("Necessary pass a lifecycleowner to Coroutines Cache Constructor") + } + + return getFromCache(key)?.let { + async { it } + }?:run{ + getFromSource(source, key, true) + } + } + + @PublishedApi internal fun getFromSource(source: suspend ()->Deferred, key: String, isLifecycle: Boolean = false):Deferred{ return async { val result = source().await() database.getDatabase().apply { beginTransaction() - copyToRealmOrUpdate(Cache(key, Gson().toJson(result), Calendar.getInstance().time)) + copyToRealmOrUpdate(Cache(key, Gson().toJson(result), Calendar.getInstance().time, isLifecycle)) commitTransaction() close() } @@ -78,10 +112,10 @@ open class CoroutinesCache(private var context: Context, val test: Boolean = fal } } - @PublishedApi internal inline fun getFromCacheValidateTime(key: String, lifeCache: CachePolicy.LifeCache): T? { + @PublishedApi internal inline fun 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(lifeCache.duration, lifeCache.timeUnit)){ + if(resultDb.date.isValidCache(timeCache.duration, timeCache.timeUnit)){ val listType = object : TypeToken() {}.type Gson().fromJson(resultDb.data, listType) as T }else{ diff --git a/library/src/main/java/io/coroutines/cache/dao/LocalDatabase.kt b/library/src/main/java/io/coroutines/cache/dao/LocalDatabase.kt index 5cecf9e..af0bd76 100644 --- a/library/src/main/java/io/coroutines/cache/dao/LocalDatabase.kt +++ b/library/src/main/java/io/coroutines/cache/dao/LocalDatabase.kt @@ -1,10 +1,12 @@ package io.coroutines.cache.dao import android.content.Context +import com.google.gson.Gson import io.realm.Realm import io.realm.RealmConfiguration import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import io.realm.kotlin.delete import java.util.* class RealmDatabase(var context: Context) { @@ -25,11 +27,30 @@ class RealmDatabase(var context: Context) { return Realm.getDefaultInstance() } + fun deleteItem(key:String){ + getDatabase().executeTransaction { + val result = it.where(Cache::class.java).equalTo("id", key).findAll() + result.deleteAllFromRealm() + } + } + + fun deleteLifecycle(){ + getDatabase().executeTransaction { + val result = it.where(Cache::class.java).equalTo("lifecycle", true).findAll() + result.deleteAllFromRealm() + } + } + + fun clear(){ + getDatabase().executeTransaction { + it.deleteAll() + } + } + companion object { private const val VERSION = 1L private const val CACHE_PREFIX = "cache" } - } open class Cache constructor(): RealmObject(){ @@ -38,10 +59,12 @@ open class Cache constructor(): RealmObject(){ var id:String = "" var data:String = "" var date:Date = Calendar.getInstance().time + var lifecycle:Boolean = false - constructor(id:String, data:String, date: Date) :this(){ + constructor(id:String, data:String, date: Date, lifecycle:Boolean = false) :this(){ this.id = id this.data = data this.date = date + this.lifecycle = lifecycle } } \ No newline at end of file