Skip to content
Permalink
Browse files

Use process scope for big operations

We now inject a CoroutineScope which is based
off the ProcessLifecycleOwner lifecycle.
Flow/observable collections still happen in the
`viewModelScope`.

Closes #360
  • Loading branch information...
chrisbanes committed Aug 13, 2019
1 parent c248429 commit 4a565ac6e2283762e4e5cb8eb4f37f9562633536
@@ -24,11 +24,13 @@ import app.tivi.interactors.launchInteractor
import app.tivi.trakt.TraktAuthState
import app.tivi.trakt.TraktManager
import app.tivi.TiviMvRxViewModel
import app.tivi.inject.ProcessLifetime
import app.tivi.interactors.launchObserve
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -40,7 +42,8 @@ class HomeActivityViewModel @AssistedInject constructor(
@Assisted initialState: HomeActivityViewState,
private val traktManager: TraktManager,
private val updateUserDetails: UpdateUserDetails,
observeUserDetails: ObserveUserDetails
observeUserDetails: ObserveUserDetails,
@ProcessLifetime private val dataOperationScope: CoroutineScope
) : TiviMvRxViewModel<HomeActivityViewState>(initialState) {
init {
viewModelScope.launchObserve(observeUserDetails) {
@@ -55,7 +58,7 @@ class HomeActivityViewModel @AssistedInject constructor(
.distinctUntilChanged()
.onEach {
if (it == TraktAuthState.LOGGED_IN) {
viewModelScope.launchInteractor(updateUserDetails,
dataOperationScope.launchInteractor(updateUserDetails,
UpdateUserDetails.Params("me", false))
}
}
@@ -19,13 +19,16 @@ package app.tivi.inject
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.coroutineScope
import androidx.preference.PreferenceManager
import app.tivi.BuildConfig
import app.tivi.TiviApplication
import app.tivi.util.AppCoroutineDispatchers
import dagger.Module
import dagger.Provides
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.threeten.bp.ZoneId
import org.threeten.bp.format.DateTimeFormatter
@@ -124,4 +127,10 @@ class AppModule {
.withLocale(Locale.getDefault())
.withZone(ZoneId.systemDefault())
}

@Provides
@ProcessLifetime
fun provideLongLifetimeScope(): CoroutineScope {
return ProcessLifecycleOwner.get().lifecycle.coroutineScope
}
}
@@ -56,4 +56,9 @@ annotation class PerApplication
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
@MustBeDocumented
annotation class ApplicationId
annotation class ApplicationId

@Retention(AnnotationRetention.RUNTIME)
@Qualifier
@MustBeDocumented
annotation class ProcessLifetime
@@ -25,6 +25,7 @@ import app.tivi.data.Entry
import app.tivi.data.resultentities.EntryWithShow
import app.tivi.interactors.PagingInteractor
import app.tivi.tmdb.TmdbManager
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.combine
@@ -81,7 +82,7 @@ abstract class EntryViewModel<LI : EntryWithShow<out Entry>, PI : PagingInteract
viewModelScope.launch {
sendMessage(UiResource(Status.LOADING_MORE))
try {
callLoadMore()
callLoadMore().join()
onSuccess()
} catch (e: Exception) {
onError(e)
@@ -93,17 +94,17 @@ abstract class EntryViewModel<LI : EntryWithShow<out Entry>, PI : PagingInteract
viewModelScope.launch {
sendMessage(UiResource(Status.REFRESHING))
try {
callRefresh()
callRefresh().join()
onSuccess()
} catch (e: Exception) {
onError(e)
}
}
}

protected open suspend fun callRefresh() = Unit
protected abstract suspend fun callRefresh(): Job

protected open suspend fun callLoadMore() = Unit
protected abstract suspend fun callLoadMore(): Job

private fun onError(t: Throwable) {
logger.e(t)
@@ -17,20 +17,22 @@
package app.tivi.home.discover

import androidx.lifecycle.viewModelScope
import app.tivi.TiviMvRxViewModel
import app.tivi.inject.ProcessLifetime
import app.tivi.interactors.ObservePopularShows
import app.tivi.interactors.ObserveTrendingShows
import app.tivi.interactors.UpdatePopularShows
import app.tivi.interactors.UpdateTrendingShows
import app.tivi.interactors.launchInteractor
import app.tivi.tmdb.TmdbManager
import app.tivi.TiviMvRxViewModel
import app.tivi.interactors.launchObserve
import app.tivi.tmdb.TmdbManager
import app.tivi.util.ObservableLoadingCounter
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@@ -41,9 +43,9 @@ class DiscoverViewModel @AssistedInject constructor(
observePopularShows: ObservePopularShows,
private val updateTrendingShows: UpdateTrendingShows,
observeTrendingShows: ObserveTrendingShows,
tmdbManager: TmdbManager
tmdbManager: TmdbManager,
@ProcessLifetime private val dataOperationScope: CoroutineScope
) : TiviMvRxViewModel<DiscoverViewState>(initialState) {

private val trendingLoadingState = ObservableLoadingCounter()
private val popularLoadingState = ObservableLoadingCounter()

@@ -84,12 +86,12 @@ class DiscoverViewModel @AssistedInject constructor(
}

fun refresh() {
viewModelScope.launchInteractor(
dataOperationScope.launchInteractor(
updatePopularShows,
UpdatePopularShows.Params(UpdatePopularShows.Page.REFRESH),
popularLoadingState
)
viewModelScope.launchInteractor(
dataOperationScope.launchInteractor(
updateTrendingShows,
UpdateTrendingShows.Params(UpdateTrendingShows.Page.REFRESH),
trendingLoadingState
@@ -28,13 +28,15 @@ import app.tivi.interactors.launchInteractor
import app.tivi.episodedetails.EpisodeDetailsViewState.Action
import app.tivi.tmdb.TmdbManager
import app.tivi.TiviMvRxViewModel
import app.tivi.inject.ProcessLifetime
import app.tivi.interactors.launchObserve
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import org.threeten.bp.OffsetDateTime
@@ -47,7 +49,8 @@ class EpisodeDetailsViewModel @AssistedInject constructor(
private val addEpisodeWatch: AddEpisodeWatch,
private val removeEpisodeWatches: RemoveEpisodeWatches,
private val removeEpisodeWatch: RemoveEpisodeWatch,
tmdbManager: TmdbManager
tmdbManager: TmdbManager,
@ProcessLifetime private val dataOperationScope: CoroutineScope
) : TiviMvRxViewModel<EpisodeDetailsViewState>(initialState) {
init {
viewModelScope.launchObserve(observeEpisodeDetails) {
@@ -79,23 +82,31 @@ class EpisodeDetailsViewModel @AssistedInject constructor(
}

private fun refresh() = withState {
viewModelScope.launchInteractor(updateEpisodeDetails, UpdateEpisodeDetails.Params(it.episodeId, true))
dataOperationScope.launchInteractor(
updateEpisodeDetails,
UpdateEpisodeDetails.Params(it.episodeId, true)
)
}

fun removeWatchEntry(entry: EpisodeWatchEntry) {
viewModelScope.launchInteractor(removeEpisodeWatch, RemoveEpisodeWatch.Params(entry.id))
dataOperationScope.launchInteractor(
removeEpisodeWatch,
RemoveEpisodeWatch.Params(entry.id)
)
}

fun markWatched() {
withState {
viewModelScope.launchInteractor(addEpisodeWatch, AddEpisodeWatch.Params(it.episodeId, OffsetDateTime.now()))
}
fun markWatched() = withState {
dataOperationScope.launchInteractor(
addEpisodeWatch,
AddEpisodeWatch.Params(it.episodeId, OffsetDateTime.now())
)
}

fun markUnwatched() {
withState {
viewModelScope.launchInteractor(removeEpisodeWatches, RemoveEpisodeWatches.Params(it.episodeId))
}
fun markUnwatched() = withState {
dataOperationScope.launchInteractor(
removeEpisodeWatches,
RemoveEpisodeWatches.Params(it.episodeId)
)
}

@AssistedInject.Factory
@@ -22,6 +22,7 @@ import app.tivi.TiviMvRxViewModel
import app.tivi.data.entities.RefreshType
import app.tivi.data.entities.SortOption
import app.tivi.data.resultentities.FollowedShowEntryWithShow
import app.tivi.inject.ProcessLifetime
import app.tivi.interactors.ObservePagedFollowedShows
import app.tivi.interactors.UpdateFollowedShows
import app.tivi.interactors.launchInteractor
@@ -35,6 +36,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
@@ -45,7 +47,8 @@ class FollowedViewModel @AssistedInject constructor(
private val updateFollowedShows: UpdateFollowedShows,
private val observePagedFollowedShows: ObservePagedFollowedShows,
private val traktManager: TraktManager,
tmdbManager: TmdbManager
tmdbManager: TmdbManager,
@ProcessLifetime private val dataOperationScope: CoroutineScope
) : TiviMvRxViewModel<FollowedViewState>(initialState) {
private val boundaryCallback = object : PagedList.BoundaryCallback<FollowedShowEntryWithShow>() {
override fun onZeroItemsLoaded() {
@@ -131,10 +134,11 @@ class FollowedViewModel @AssistedInject constructor(
}

private fun refreshFollowed(fromUserInteraction: Boolean) {
loadingState.addLoader()
viewModelScope.launchInteractor(updateFollowedShows,
UpdateFollowedShows.Params(fromUserInteraction, RefreshType.QUICK))
.invokeOnCompletion { loadingState.removeLoader() }
dataOperationScope.launchInteractor(
updateFollowedShows,
UpdateFollowedShows.Params(fromUserInteraction, RefreshType.QUICK),
loadingState
)
}

@AssistedInject.Factory
@@ -18,15 +18,15 @@ package app.tivi.home.popular

import androidx.lifecycle.viewModelScope
import app.tivi.data.resultentities.PopularEntryWithShow
import app.tivi.inject.ProcessLifetime
import app.tivi.interactors.ObservePagedPopularShows
import app.tivi.interactors.UpdatePopularShows
import app.tivi.interactors.UpdatePopularShows.Page.NEXT_PAGE
import app.tivi.interactors.UpdatePopularShows.Page.REFRESH
import app.tivi.interactors.execute
import app.tivi.interactors.launchInteractor
import app.tivi.tmdb.TmdbManager
import app.tivi.util.AppCoroutineDispatchers
import app.tivi.util.EntryViewModel
import app.tivi.util.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject

@@ -35,7 +35,8 @@ class PopularShowsViewModel @Inject constructor(
private val interactor: UpdatePopularShows,
observePagedPopularShows: ObservePagedPopularShows,
tmdbManager: TmdbManager,
logger: Logger
logger: Logger,
@ProcessLifetime private val dataOperationScope: CoroutineScope
) : EntryViewModel<PopularEntryWithShow, ObservePagedPopularShows>(
dispatchers,
observePagedPopularShows,
@@ -48,7 +49,13 @@ class PopularShowsViewModel @Inject constructor(
}
}

override suspend fun callLoadMore() = interactor.execute(UpdatePopularShows.Params(NEXT_PAGE))
override suspend fun callLoadMore() = dataOperationScope.launchInteractor(
interactor,
UpdatePopularShows.Params(UpdatePopularShows.Page.NEXT_PAGE)
)

override suspend fun callRefresh() = interactor.execute(UpdatePopularShows.Params(REFRESH))
override suspend fun callRefresh() = dataOperationScope.launchInteractor(
interactor,
UpdatePopularShows.Params(UpdatePopularShows.Page.REFRESH)
)
}

0 comments on commit 4a565ac

Please sign in to comment.
You can’t perform that action at this time.