diff --git a/README.md b/README.md index f6352eddb..726de1f45 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Libraries Used * [Layout][35] - Lay out widgets using different algorithms. * Third party * [Glide][90] for image loading + * [Kotlin Coroutines][91] for managing background threads with simplified code and reducing needs for callbacks [0]: https://developer.android.com/jetpack/foundation/ [1]: https://developer.android.com/topic/libraries/support-library/packages#v7-appcompat @@ -83,6 +84,7 @@ Libraries Used [34]: https://developer.android.com/guide/components/fragments [35]: https://developer.android.com/guide/topics/ui/declaring-layout [90]: https://bumptech.github.io/glide/ +[91]: https://kotlinlang.org/docs/reference/coroutines-overview.html Upcoming features ----------------- diff --git a/app/build.gradle b/app/build.gradle index 48e68f43a..1a8483237 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,6 +63,8 @@ dependencies { implementation "com.google.android.material:material:$rootProject.materialVersion" implementation "com.google.code.gson:gson:$rootProject.gsonVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$rootProject.kotlinVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutinesVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutinesVersion" // Testing dependencies androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index b66ee8e7c..4965c37a4 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -23,3 +23,12 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile + +# ServiceLoader support +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} + +# Most of volatile fields are updated with AFU and should not be mangled +-keepclassmembernames class kotlinx.** { + volatile ; +} \ No newline at end of file diff --git a/app/src/main/java/com/google/samples/apps/sunflower/data/GardenPlantingRepository.kt b/app/src/main/java/com/google/samples/apps/sunflower/data/GardenPlantingRepository.kt index 8489d8a09..e6cbdae6d 100644 --- a/app/src/main/java/com/google/samples/apps/sunflower/data/GardenPlantingRepository.kt +++ b/app/src/main/java/com/google/samples/apps/sunflower/data/GardenPlantingRepository.kt @@ -16,21 +16,22 @@ package com.google.samples.apps.sunflower.data -import com.google.samples.apps.sunflower.utilities.runOnIoThread +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.withContext class GardenPlantingRepository private constructor( private val gardenPlantingDao: GardenPlantingDao ) { - fun createGardenPlanting(plantId: String) { - runOnIoThread { + suspend fun createGardenPlanting(plantId: String) { + withContext(IO) { val gardenPlanting = GardenPlanting(plantId) gardenPlantingDao.insertGardenPlanting(gardenPlanting) } } - fun removeGardenPlanting(gardenPlanting: GardenPlanting) { - runOnIoThread { + suspend fun removeGardenPlanting(gardenPlanting: GardenPlanting) { + withContext(IO) { gardenPlantingDao.deleteGardenPlanting(gardenPlanting) } } diff --git a/app/src/main/java/com/google/samples/apps/sunflower/utilities/AppExecutors.kt b/app/src/main/java/com/google/samples/apps/sunflower/utilities/AppExecutors.kt deleted file mode 100644 index c93d40d51..000000000 --- a/app/src/main/java/com/google/samples/apps/sunflower/utilities/AppExecutors.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.sunflower.utilities - -import java.util.concurrent.Executors - -private val IO_EXECUTOR = Executors.newSingleThreadExecutor() - -/** - * Utility method to run blocks on a dedicated background thread, used for io/database work. - */ -fun runOnIoThread(f: () -> Unit) { - IO_EXECUTOR.execute(f) -} \ No newline at end of file diff --git a/app/src/main/java/com/google/samples/apps/sunflower/viewmodels/PlantDetailViewModel.kt b/app/src/main/java/com/google/samples/apps/sunflower/viewmodels/PlantDetailViewModel.kt index 7427ff101..51b1a1f53 100644 --- a/app/src/main/java/com/google/samples/apps/sunflower/viewmodels/PlantDetailViewModel.kt +++ b/app/src/main/java/com/google/samples/apps/sunflower/viewmodels/PlantDetailViewModel.kt @@ -23,6 +23,10 @@ import com.google.samples.apps.sunflower.PlantDetailFragment import com.google.samples.apps.sunflower.data.GardenPlantingRepository import com.google.samples.apps.sunflower.data.Plant import com.google.samples.apps.sunflower.data.PlantRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch /** * The ViewModel used in [PlantDetailFragment]. @@ -36,6 +40,29 @@ class PlantDetailViewModel( val isPlanted: LiveData val plant: LiveData + /** + * This is the job for all coroutines started by this ViewModel. + * + * Cancelling this job will cancel all coroutines started by this ViewModel. + */ + private val viewModelJob = Job() + + /** + * This is the scope for all coroutines launched by [PlantDetailViewModel]. + * + * Since we pass [viewModelJob], you can cancel all coroutines launched by [viewModelScope] by calling + * viewModelJob.cancel(). This is called in [onCleared]. + */ + private val viewModelScope = CoroutineScope(Main + viewModelJob) + + /** + * Cancel all coroutines when the ViewModel is cleared. + */ + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } + init { /* The getGardenPlantingForPlant method returns a LiveData from querying the database. The @@ -49,6 +76,8 @@ class PlantDetailViewModel( } fun addPlantToGarden() { - gardenPlantingRepository.createGardenPlanting(plantId) + viewModelScope.launch { + gardenPlantingRepository.createGardenPlanting(plantId) + } } } diff --git a/build.gradle b/build.gradle index 34dbce975..904aaec9e 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ buildscript { // App dependencies constraintLayoutVersion = '2.0.0-alpha2' coreTestingVersion = '2.0.0' + coroutinesVersion = "1.0.1" espressoVersion = '3.1.0-alpha4' glideVersion = '4.8.0' gradleVersion = '3.2.1'