Skip to content
This repository was archived by the owner on Jan 11, 2024. It is now read-only.

Commit e94638a

Browse files
Use ViewmodelScope instead of creating coroutine scope
* Use viewModelScope to call the database operations. * Remove Dispatcher.IO context and coroutine scope for @dao methods. Room library automatically moves database calls onto a background thread, so you don't explicitly need to move them to a background thread. A suspend @dao method and a `@Query` method returning LiveData in Room automatically uses a background thread for database calls. You don't have to explicitly specify the Dispatcher.IO. Room does it for you in generated implementation.
1 parent 77fff5c commit e94638a

File tree

37 files changed

+161
-494
lines changed

37 files changed

+161
-494
lines changed

DevBytes-starter/app/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ android {
2828
targetSdkVersion 30
2929
versionCode 1
3030
versionName "1.0"
31-
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
3231
vectorDrawables.useSupportLibrary = true
32+
multiDexEnabled true
3333
}
3434
buildTypes {
3535
release {
@@ -54,7 +54,7 @@ dependencies {
5454
implementation 'com.google.android.material:material:1.0.0'
5555

5656
// Android KTX
57-
implementation 'androidx.core:core-ktx:1.0.2'
57+
implementation 'androidx.core:core-ktx:1.3.1'
5858

5959
// constraint layout
6060
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
@@ -87,6 +87,7 @@ dependencies {
8787
// ViewModel and LiveData
8888
def lifecycle_version = "2.2.0"
8989
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
90+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
9091

9192
// logging
9293
implementation 'com.jakewharton.timber:timber:4.7.1'

DevBytes-starter/app/src/main/java/com/example/android/devbyteviewer/viewmodels/DevByteViewModel.kt

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.lifecycle.LiveData
2222
import androidx.lifecycle.MutableLiveData
2323
import androidx.lifecycle.ViewModel
2424
import androidx.lifecycle.ViewModelProvider
25+
import androidx.lifecycle.viewModelScope
2526
import com.example.android.devbyteviewer.domain.DevByteVideo
2627
import com.example.android.devbyteviewer.network.DevByteNetwork
2728
import com.example.android.devbyteviewer.network.asDomainModel
@@ -40,21 +41,6 @@ import java.io.IOException
4041
*/
4142
class DevByteViewModel(application: Application) : AndroidViewModel(application) {
4243

43-
/**
44-
* This is the job for all coroutines started by this ViewModel.
45-
*
46-
* Cancelling this job will cancel all coroutines started by this ViewModel.
47-
*/
48-
private val viewModelJob = SupervisorJob()
49-
50-
/**
51-
* This is the main scope for all coroutines launched by MainViewModel.
52-
*
53-
* Since we pass viewModelJob, you can cancel all coroutines launched by uiScope by calling
54-
* viewModelJob.cancel()
55-
*/
56-
private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Main)
57-
5844
/**
5945
* A playlist of videos that can be shown on the screen. This is private to avoid exposing a
6046
* way to set this value to observers.
@@ -129,15 +115,6 @@ class DevByteViewModel(application: Application) : AndroidViewModel(application)
129115
_isNetworkErrorShown.value = true
130116
}
131117

132-
133-
/**
134-
* Cancel all coroutines when the ViewModel is cleared
135-
*/
136-
override fun onCleared() {
137-
super.onCleared()
138-
viewModelJob.cancel()
139-
}
140-
141118
/**
142119
* Factory for constructing DevByteViewModel with parameter
143120
*/

RecyclerViewClickHandler-Starter/app/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ dependencies {
6363
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
6464
kapt "androidx.room:room-compiler:$room_version"
6565
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
66+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
67+
68+
// Kotlin Extensions and Coroutines support for Room
69+
implementation "androidx.room:room-ktx:$room_version"
6670

6771
// Coroutines
6872
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"

RecyclerViewClickHandler-Starter/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import androidx.room.Update
3131
interface SleepDatabaseDao {
3232

3333
@Insert
34-
fun insert(night: SleepNight)
34+
suspend fun insert(night: SleepNight)
3535

3636
/**
3737
* When updating a row with a value already set in a column,
@@ -40,23 +40,23 @@ interface SleepDatabaseDao {
4040
* @param night new value to write
4141
*/
4242
@Update
43-
fun update(night: SleepNight)
43+
suspend fun update(night: SleepNight)
4444

4545
/**
4646
* Selects and returns the row that matches the supplied start time, which is our key.
4747
*
4848
* @param key startTimeMilli to match
4949
*/
5050
@Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
51-
fun get(key: Long): SleepNight
51+
suspend fun get(key: Long): SleepNight
5252

5353
/**
5454
* Deletes all values from the table.
5555
*
5656
* This does not delete the table, only its contents.
5757
*/
5858
@Query("DELETE FROM daily_sleep_quality_table")
59-
fun clear()
59+
suspend fun clear()
6060

6161
/**
6262
* Selects and returns all rows in the table,
@@ -70,7 +70,7 @@ interface SleepDatabaseDao {
7070
* Selects and returns the latest night.
7171
*/
7272
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
73-
fun getTonight(): SleepNight?
73+
suspend fun getTonight(): SleepNight?
7474

7575
/**
7676
* Selects and returns the night with given nightId.

RecyclerViewClickHandler-Starter/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class SleepDetailFragment : Fragment() {
6767
binding.setLifecycleOwner(this)
6868

6969
// Add an Observer to the state variable for Navigating when a Quality icon is tapped.
70-
sleepDetailViewModel.navigateToSleepTracker.observe(this, Observer {
70+
sleepDetailViewModel.navigateToSleepTracker.observe(viewLifecycleOwner, Observer {
7171
if (it == true) { // Observed state is true.
7272
this.findNavController().navigate(
7373
SleepDetailFragmentDirections.actionSleepDetailFragmentToSleepTrackerFragment())

RecyclerViewClickHandler-Starter/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ class SleepDetailViewModel(
4242
*/
4343
val database = dataSource
4444

45-
/** Coroutine setup variables */
46-
47-
/**
48-
* viewModelJob allows us to cancel all coroutines started by this ViewModel.
49-
*/
50-
private val viewModelJob = Job()
51-
5245
private val night: LiveData<SleepNight>
5346

5447
fun getNight() = night
@@ -72,16 +65,6 @@ class SleepDetailViewModel(
7265
val navigateToSleepTracker: LiveData<Boolean?>
7366
get() = _navigateToSleepTracker
7467

75-
/**
76-
* Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
77-
*
78-
* onCleared() gets called when the ViewModel is destroyed.
79-
*/
80-
override fun onCleared() {
81-
super.onCleared()
82-
viewModelJob.cancel()
83-
}
84-
8568

8669
/**
8770
* Call this immediately after navigating to [SleepTrackerFragment]

RecyclerViewClickHandler-Starter/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class SleepQualityFragment : Fragment() {
7070
binding.sleepQualityViewModel = sleepQualityViewModel
7171

7272
// Add an Observer to the state variable for Navigating when a Quality icon is tapped.
73-
sleepQualityViewModel.navigateToSleepTracker.observe(this, Observer {
73+
sleepQualityViewModel.navigateToSleepTracker.observe(viewLifecycleOwner, Observer {
7474
if (it == true) { // Observed state is true.
7575
this.findNavController().navigate(
7676
SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment())

RecyclerViewClickHandler-Starter/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,9 @@ package com.example.android.trackmysleepquality.sleepquality
1919
import androidx.lifecycle.LiveData
2020
import androidx.lifecycle.MutableLiveData
2121
import androidx.lifecycle.ViewModel
22+
import androidx.lifecycle.viewModelScope
2223
import com.example.android.trackmysleepquality.database.SleepDatabaseDao
23-
import kotlinx.coroutines.CoroutineScope
24-
import kotlinx.coroutines.Dispatchers
25-
import kotlinx.coroutines.Job
2624
import kotlinx.coroutines.launch
27-
import kotlinx.coroutines.withContext
2825

2926
/**
3027
* ViewModel for SleepQualityFragment.
@@ -40,25 +37,6 @@ class SleepQualityViewModel(
4037
*/
4138
val database = dataSource
4239

43-
/** Coroutine setup variables */
44-
45-
/**
46-
* viewModelJob allows us to cancel all coroutines started by this ViewModel.
47-
*/
48-
private val viewModelJob = Job()
49-
50-
/**
51-
* A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
52-
*
53-
* Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled
54-
* by calling `viewModelJob.cancel()`
55-
*
56-
* By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
57-
* the main thread on Android. This is a sensible default because most coroutines started by
58-
* a [ViewModel] update the UI after performing some processing.
59-
*/
60-
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
61-
6240
/**
6341
* Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
6442
*
@@ -73,17 +51,6 @@ class SleepQualityViewModel(
7351
val navigateToSleepTracker: LiveData<Boolean?>
7452
get() = _navigateToSleepTracker
7553

76-
/**
77-
* Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
78-
*
79-
* onCleared() gets called when the ViewModel is destroyed.
80-
*/
81-
override fun onCleared() {
82-
super.onCleared()
83-
viewModelJob.cancel()
84-
}
85-
86-
8754
/**
8855
* Call this immediately after navigating to [SleepTrackerFragment]
8956
*/
@@ -97,14 +64,10 @@ class SleepQualityViewModel(
9764
* Then navigates back to the SleepTrackerFragment.
9865
*/
9966
fun onSetSleepQuality(quality: Int) {
100-
uiScope.launch {
101-
// IO is a thread pool for running operations that access the disk, such as
102-
// our Room database.
103-
withContext(Dispatchers.IO) {
104-
val tonight = database.get(sleepNightKey)
105-
tonight.sleepQuality = quality
106-
database.update(tonight)
107-
}
67+
viewModelScope.launch {
68+
val tonight = database.get(sleepNightKey)
69+
tonight.sleepQuality = quality
70+
database.update(tonight)
10871

10972
// Setting this state variable to true will alert the observer and trigger navigation.
11073
_navigateToSleepTracker.value = true

RecyclerViewClickHandler-Starter/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ class SleepTrackerFragment : Fragment() {
8585

8686
// Add an Observer on the state variable for showing a Snackbar message
8787
// when the CLEAR button is pressed.
88-
sleepTrackerViewModel.showSnackBarEvent.observe(this, Observer {
88+
sleepTrackerViewModel.showSnackBarEvent.observe(viewLifecycleOwner, Observer {
8989
if (it == true) { // Observed state is true.
9090
Snackbar.make(
91-
activity!!.findViewById(android.R.id.content),
91+
requireActivity().findViewById(android.R.id.content),
9292
getString(R.string.cleared_message),
9393
Snackbar.LENGTH_SHORT // How long to display the message.
9494
).show()
@@ -99,7 +99,7 @@ class SleepTrackerFragment : Fragment() {
9999
})
100100

101101
// Add an Observer on the state variable for Navigating when STOP button is pressed.
102-
sleepTrackerViewModel.navigateToSleepQuality.observe(this, Observer { night ->
102+
sleepTrackerViewModel.navigateToSleepQuality.observe(viewLifecycleOwner, Observer { night ->
103103
night?.let {
104104
// We need to get the navController from this, because button is not ready, and it
105105
// just has to be a view. For some reason, this only matters if we hit stop again

0 commit comments

Comments
 (0)