From ae55ca7dfc6681f134d81199d1db8a7f17dec8dd Mon Sep 17 00:00:00 2001 From: lukaslechner Date: Wed, 10 Jun 2020 10:54:41 +0200 Subject: [PATCH 01/29] Clear implementations for branch "coroutines_course_empty" --- .../PerformSingleNetworkRequestViewModel.kt | 14 +---- .../CalculationInBackgroundViewModel.kt | 38 ------------ .../CooperativeCancellationViewModel.kt | 57 ++--------------- ...CalculationInSeveralCoroutinesViewModel.kt | 31 ---------- .../usecase13/ExceptionHandlingViewModel.kt | 46 -------------- ...eCoroutineWhenUserLeavesScreenViewModel.kt | 30 +-------- ...form2SequentialNetworkRequestsViewModel.kt | 15 ----- ...entialNetworkRequestsCallbacksViewModel.kt | 53 ---------------- .../SequentialNetworkRequestsRxViewModel.kt | 29 --------- ...ormNetworkRequestsConcurrentlyViewModel.kt | 62 ------------------- ...ariableAmountOfNetworkRequestsViewModel.kt | 31 +--------- .../NetworkRequestWithTimeoutViewModel.kt | 40 ------------ .../usecase6/RetryNetworkRequestViewModel.kt | 38 +----------- .../usecase7/TimeoutAndRetryViewModel.kt | 58 ----------------- .../usecase8/RoomAndCoroutinesViewModel.kt | 28 +-------- .../usecase9/DebuggingCoroutinesViewModel.kt | 43 ------------- 16 files changed, 10 insertions(+), 603 deletions(-) diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModel.kt index 90c6a061..ae782c7e 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModel.kt @@ -1,25 +1,13 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.launch -import timber.log.Timber class PerformSingleNetworkRequestViewModel( private val mockApi: MockApi = mockApi() ) : BaseViewModel() { fun performSingleNetworkRequest() { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - val recentAndroidVersions = mockApi.getRecentAndroidVersions() - uiState.value = UiState.Success(recentAndroidVersions) - } catch (exception: Exception) { - Timber.e(exception) - uiState.value = UiState.Error("Network Request failed!") - } - } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt index f61e5f3c..0ec10a56 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt @@ -1,52 +1,14 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase10 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.math.BigInteger -import kotlin.system.measureTimeMillis class CalculationInBackgroundViewModel( private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default ) : BaseViewModel() { fun performCalculation(factorialOf: Int) { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - var result: BigInteger = BigInteger.ZERO - val computationDuration = measureTimeMillis { - result = calculateFactorialOf(factorialOf) - } - var resultString = "" - val stringConversionDuration = measureTimeMillis { - resultString = convertToString(result) - } - - uiState.value = - UiState.Success(resultString, computationDuration, stringConversionDuration) - } catch (exception: Exception) { - UiState.Error("Error while calculating result") - } - } } - - // factorial of n (n!) = 1 * 2 * 3 * 4 * ... * n - private suspend fun calculateFactorialOf(number: Int): BigInteger = - withContext(defaultDispatcher) { - var factorial = BigInteger.ONE - for (i in 1..number) { - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - } - factorial - } - - private suspend fun convertToString(number: BigInteger): String = - withContext(defaultDispatcher) { - number.toString() - } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase11/CooperativeCancellationViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase11/CooperativeCancellationViewModel.kt index 639ed93a..1dbe3178 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase11/CooperativeCancellationViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase11/CooperativeCancellationViewModel.kt @@ -3,71 +3,22 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.* -import java.math.BigInteger -import kotlin.system.measureTimeMillis +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers class CooperativeCancellationViewModel( private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default ) : ViewModel() { - private var calculationJob: Job? = null - fun performCalculation(factorialOf: Int) { - uiState.value = UiState.Loading - calculationJob = viewModelScope.launch { - try { - - var result: BigInteger = BigInteger.ZERO - val computationDuration = measureTimeMillis { - result = calculateFactorialOf(factorialOf) - } - - var resultString = "" - val stringConversionDuration = measureTimeMillis { - resultString = convertToString(result) - } - uiState.value = - UiState.Success(resultString, computationDuration, stringConversionDuration) - } catch (exception: Exception) { - uiState.value = if (exception is CancellationException) { - UiState.Error("Calculation was cancelled") - } else { - UiState.Error("Error while calculating result") - } - } - } } - // factorial of n (n!) = 1 * 2 * 3 * 4 * ... * n - private suspend fun calculateFactorialOf(number: Int): BigInteger = - withContext(defaultDispatcher) { - var factorial = BigInteger.ONE - for (i in 1..number) { - - // yield enables cooperative cancellations - // alternatives: - // - ensureActive() - // - isActive() - possible to do clean up tasks with - yield() - - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - } - factorial - } + fun cancelCalculation() { - private suspend fun convertToString(number: BigInteger): String = - withContext(defaultDispatcher) { - number.toString() - } + } fun uiState(): LiveData = uiState - fun cancelCalculation() { - calculationJob?.cancel() - } - private val uiState: MutableLiveData = MutableLiveData() } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt index 376693a5..de3b0c15 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt @@ -1,13 +1,8 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase12 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.math.BigInteger -import kotlin.system.measureTimeMillis class CalculationInSeveralCoroutinesViewModel( private val factorialCalculator: FactorialCalculator = FactorialCalculator(), @@ -18,32 +13,6 @@ class CalculationInSeveralCoroutinesViewModel( factorialOf: Int, numberOfCoroutines: Int ) { - uiState.value = UiState.Loading - viewModelScope.launch { - var factorialResult = BigInteger.ZERO - val computationDuration = measureTimeMillis { - factorialResult = - factorialCalculator.calculateFactorial( - factorialOf, - numberOfCoroutines - ) - } - - var resultString = "" - val stringConversionDuration = measureTimeMillis { - resultString = convertToString(factorialResult) - } - - uiState.value = - UiState.Success(resultString, computationDuration, stringConversionDuration) - } } - - private suspend fun convertToString( - number: BigInteger - ): String = - withContext(defaultDispatcher) { - number.toString() - } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase13/ExceptionHandlingViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase13/ExceptionHandlingViewModel.kt index a4e98d83..e45bad1f 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase13/ExceptionHandlingViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase13/ExceptionHandlingViewModel.kt @@ -1,6 +1,5 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase13 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi import kotlinx.coroutines.* @@ -11,59 +10,14 @@ class ExceptionHandlingViewModel( ) : BaseViewModel() { fun handleExceptionWithTryCatch() { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - api.getAndroidVersionFeatures(27) - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed: $exception") - } - } } fun handleWithCoroutineExceptionHandler() { - val exceptionHandler = CoroutineExceptionHandler { _, exception -> - uiState.value = UiState.Error("Network Request failed!! $exception") - } - uiState.value = UiState.Loading - viewModelScope.launch(exceptionHandler) { - api.getAndroidVersionFeatures(27) - } } fun showResultsEvenIfChildCoroutineFails() { - uiState.value = UiState.Loading - viewModelScope.launch { - supervisorScope { - val oreoFeaturesDeferred = async { api.getAndroidVersionFeatures(27) } - val pieFeaturesDeferred = async { api.getAndroidVersionFeatures(28) } - val android10FeaturesDeferred = async { api.getAndroidVersionFeatures(29) } - - val versionFeatures = listOf( - oreoFeaturesDeferred, - pieFeaturesDeferred, - android10FeaturesDeferred - ).mapNotNull { - try { - it.await() - } catch (exception: Exception) { - // We have to re-throw cancellation exceptions so that - // our Coroutine gets cancelled immediately. - // Otherwise, the CancellationException is ignored - // and the Coroutine keeps running until it reaches the next - // suspension point. - if (exception is CancellationException) { - throw exception - } - Timber.e("Error loading feature data!") - null - } - } - uiState.value = UiState.Success(versionFeatures) - } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt index 0a4aee7a..3c228f8e 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt @@ -1,45 +1,17 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase14 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import kotlinx.coroutines.launch class ContinueCoroutineWhenUserLeavesScreenViewModel( private var repository: AndroidVersionRepository ) : BaseViewModel() { - // more information in this blogpost about "Coroutines & Patterns for work that shouldn't - // be cancelled" => - // https://medium.com/androiddevelopers/coroutines-patterns-for-work-that-shouldnt-be-cancelled-e26c40f142ad - fun loadData() { - uiState.value = UiState.Loading.LoadFromDb - - viewModelScope.launch { - val localVersions = repository.getLocalAndroidVersions() - if (localVersions.isNotEmpty()) { - uiState.value = - UiState.Success(DataSource.Database, localVersions) - } else { - uiState.value = - UiState.Error(DataSource.Database, "Database empty!") - } - uiState.value = UiState.Loading.LoadFromNetwork - - try { - uiState.value = UiState.Success( - DataSource.Network, - repository.loadAndStoreRemoteAndroidVersions() - ) - } catch (exception: Exception) { - uiState.value = UiState.Error(DataSource.Network, "Network Request failed") - } - } } fun clearDatabase() { - repository.clearDatabase() + } } diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModel.kt index 2cc394f5..ca3c84d6 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModel.kt @@ -1,28 +1,13 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.launch class Perform2SequentialNetworkRequestsViewModel( private val mockApi: MockApi = mockApi() ) : BaseViewModel() { fun perform2SequentialNetworkRequest() { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - val recentVersions = mockApi.getRecentAndroidVersions() - val mostRecentVersion = recentVersions.last() - val featuresOfMostRecentVersion = - mockApi.getAndroidVersionFeatures(mostRecentVersion.apiLevel) - - uiState.value = UiState.Success(featuresOfMostRecentVersion) - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModel.kt index dbc60105..760f7398 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModel.kt @@ -1,65 +1,12 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.callbacks import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response class SequentialNetworkRequestsCallbacksViewModel( private val mockApi: CallbackMockApi = mockApi() ) : BaseViewModel() { - private var getAndroidVersionsCall: Call>? = null - private var getAndroidFeaturesCall: Call? = null - fun perform2SequentialNetworkRequest() { - uiState.value = UiState.Loading - - getAndroidVersionsCall = mockApi.getRecentAndroidVersions() - getAndroidVersionsCall!!.enqueue(object : Callback> { - override fun onFailure(call: Call>, t: Throwable) { - uiState.value = UiState.Error("Network Request failed") - } - - override fun onResponse( - call: Call>, - response: Response> - ) { - if (response.isSuccessful) { - val mostRecentVersion = response.body()!!.last() - getAndroidFeaturesCall = - mockApi.getAndroidVersionFeatures(mostRecentVersion.apiLevel) - getAndroidFeaturesCall!!.enqueue(object : Callback { - override fun onFailure(call: Call, t: Throwable) { - uiState.value = UiState.Error("Network Request failed") - } - - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - val featuresOfMostRecentVersion = response.body()!! - uiState.value = UiState.Success(featuresOfMostRecentVersion) - } else { - uiState.value = UiState.Error("Network Request failed") - } - } - }) - } else { - uiState.value = UiState.Error("Network Request failed") - } - } - }) - } - - override fun onCleared() { - super.onCleared() - - getAndroidVersionsCall?.cancel() - getAndroidFeaturesCall?.cancel() } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/rx/SequentialNetworkRequestsRxViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/rx/SequentialNetworkRequestsRxViewModel.kt index 66b4df79..cc1038a2 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/rx/SequentialNetworkRequestsRxViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/rx/SequentialNetworkRequestsRxViewModel.kt @@ -1,41 +1,12 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.rx import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo -import io.reactivex.rxkotlin.subscribeBy -import io.reactivex.schedulers.Schedulers class SequentialNetworkRequestsRxViewModel( private val mockApi: RxMockApi = mockApi() ) : BaseViewModel() { - private val disposables = CompositeDisposable() - fun perform2SequentialNetworkRequest() { - uiState.value = UiState.Loading - - mockApi.getRecentAndroidVersions() - .flatMap { androidVersions -> - val recentVersion = androidVersions.last() - mockApi.getAndroidVersionFeatures(recentVersion.apiLevel) - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { featureVersions -> - uiState.value = UiState.Success(featureVersions) - }, - onError = { - uiState.value = UiState.Error("Network Request failed.") - } - ) - .addTo(disposables) - } - override fun onCleared() { - super.onCleared() - disposables.clear() } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModel.kt index 35930fb0..f283c6f2 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModel.kt @@ -1,79 +1,17 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase3 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.launch class PerformNetworkRequestsConcurrentlyViewModel( private val mockApi: MockApi = mockApi() ) : BaseViewModel() { fun performNetworkRequestsSequentially() { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - val oreoFeatures = mockApi.getAndroidVersionFeatures(27) - val pieFeatures = mockApi.getAndroidVersionFeatures(28) - val android10Features = mockApi.getAndroidVersionFeatures(29) - val versionFeatures = listOf(oreoFeatures, pieFeatures, android10Features) - uiState.value = UiState.Success(versionFeatures) - - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - } } fun performNetworkRequestsConcurrently() { - uiState.value = UiState.Loading - - val oreoFeaturesDeferred = viewModelScope.async { mockApi.getAndroidVersionFeatures(27) } - val pieFeaturesDeferred = viewModelScope.async { mockApi.getAndroidVersionFeatures(28) } - val android10FeaturesDeferred = - viewModelScope.async { mockApi.getAndroidVersionFeatures(29) } - - viewModelScope.launch { - try { - val versionFeatures = - awaitAll(oreoFeaturesDeferred, pieFeaturesDeferred, android10FeaturesDeferred) - uiState.value = UiState.Success(versionFeatures) - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - } - - /* - - Alternatively: - - viewModelScope.launch { - try { - // we need to wrap this code with a coroutineScope block - // otherwise the app would crash on unsuccessful network requests - coroutineScope { - val oreoFeaturesDeferred = async { mockApi.getAndroidVersionFeatures(27) } - val pieFeaturesDeferred = async { mockApi.getAndroidVersionFeatures(28) } - val android10FeaturesDeferred = async { mockApi.getAndroidVersionFeatures(29) } - - val oreoFeatures = oreoFeaturesDeferred.await() - val pieFeatures = pieFeaturesDeferred.await() - val android10Features = android10FeaturesDeferred.await() - - val versionFeatures = listOf(oreoFeatures, pieFeatures, android10Features) - - // other alternative: (but slightly different behavior when a deferred fails, see docs) - // val versionFeatures = awaitAll(oreoFeaturesDeferred, pieFeaturesDeferred, android10FeaturesDeferred) - - uiState.value = UiState.Success(versionFeatures) - } - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - }*/ } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModel.kt index 1a089480..900a78b6 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModel.kt @@ -1,44 +1,17 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase4 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.launch class VariableAmountOfNetworkRequestsViewModel( private val mockApi: MockApi = mockApi() ) : BaseViewModel() { fun performNetworkRequestsSequentially() { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - val recentVersions = mockApi.getRecentAndroidVersions() - val versionFeatures = recentVersions.map { androidVersion -> - mockApi.getAndroidVersionFeatures(androidVersion.apiLevel) - } - uiState.value = UiState.Success(versionFeatures) - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - } + } fun performNetworkRequestsConcurrently() { - uiState.value = UiState.Loading - viewModelScope.launch { - try { - val recentVersions = mockApi.getRecentAndroidVersions() - val versionFeatures = recentVersions - .map { androidVersion -> - async { mockApi.getAndroidVersionFeatures(androidVersion.apiLevel) } - }.awaitAll() - uiState.value = UiState.Success(versionFeatures) - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModel.kt index 6d4cb806..3a7baba3 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModel.kt @@ -1,54 +1,14 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase5 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout -import kotlinx.coroutines.withTimeoutOrNull class NetworkRequestWithTimeoutViewModel( private val api: MockApi = mockApi() ) : BaseViewModel() { fun performNetworkRequest(timeout: Long) { - uiState.value = UiState.Loading - // usingWithTimeout(timeout) - usingWithTimeoutOrNull(timeout) - } - - private fun usingWithTimeout(timeout: Long) { - viewModelScope.launch { - try { - val recentVersions = withTimeout(timeout) { - api.getRecentAndroidVersions() - } - uiState.value = UiState.Success(recentVersions) - } catch (timeoutCancellationException: TimeoutCancellationException) { - uiState.value = UiState.Error("Network Request timed out!") - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed!") - } - } - } - - private fun usingWithTimeoutOrNull(timeout: Long) { - viewModelScope.launch { - try { - val recentVersions = withTimeoutOrNull(timeout) { - api.getRecentAndroidVersions() - } - if (recentVersions != null) { - uiState.value = UiState.Success(recentVersions) - } else { - uiState.value = UiState.Error("Network Request timed out!") - } - } catch (exception: Exception) { - uiState.value = UiState.Error("Network Request failed!") - } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModel.kt index 00b433a5..068ba751 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModel.kt @@ -1,50 +1,14 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase6 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import timber.log.Timber class RetryNetworkRequestViewModel( private val api: MockApi = mockApi() ) : BaseViewModel() { fun performNetworkRequest() { - uiState.value = UiState.Loading - viewModelScope.launch { - val numberOfRetries = 2 - try { - retry(times = numberOfRetries) { - val recentVersions = api.getRecentAndroidVersions() - uiState.value = UiState.Success(recentVersions) - } - } catch (e: Exception) { - uiState.value = UiState.Error("Network Request failed") - } - } - } - // retry with exponential backoff - // inspired by https://stackoverflow.com/questions/46872242/how-to-exponential-backoff-retry-on-kotlin-coroutines - private suspend fun retry( - times: Int, - initialDelayMillis: Long = 100, - maxDelayMillis: Long = 1000, - factor: Double = 2.0, - block: suspend () -> T - ): T { - var currentDelay = initialDelayMillis - repeat(times) { - try { - return block() - } catch (exception: Exception) { - Timber.e(exception) - } - delay(currentDelay) - currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelayMillis) - } - return block() // last attempt } + } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase7/TimeoutAndRetryViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase7/TimeoutAndRetryViewModel.kt index 1e0be0ce..c2af2322 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase7/TimeoutAndRetryViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase7/TimeoutAndRetryViewModel.kt @@ -1,71 +1,13 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase7 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.* -import timber.log.Timber class TimeoutAndRetryViewModel( private val api: MockApi = mockApi() ) : BaseViewModel() { fun performNetworkRequest() { - uiState.value = UiState.Loading - val numberOfRetries = 2 - val timeout = 1000L - val oreoVersionsDeferred = viewModelScope.async { - retryWithTimeout(numberOfRetries, timeout) { - api.getAndroidVersionFeatures(27) - } - } - - val pieVersionsDeferred = viewModelScope.async { - retryWithTimeout(numberOfRetries, timeout) { - api.getAndroidVersionFeatures(28) - } - } - - viewModelScope.launch { - try { - val versionFeatures = listOf( - oreoVersionsDeferred, - pieVersionsDeferred - ).awaitAll() - - uiState.value = UiState.Success(versionFeatures) - - } catch (e: Exception) { - Timber.e(e) - uiState.value = UiState.Error("Network Request failed") - } - } - } - - private suspend fun retryWithTimeout( - numberOfRetries: Int, - timeout: Long, - block: suspend () -> T - ) = retry(numberOfRetries) { - withTimeout(timeout) { - block() - } - } - - private suspend fun retry( - numberOfRetries: Int, - delayBetweenRetries: Long = 100, - block: suspend () -> T - ): T { - repeat(numberOfRetries) { - try { - return block() - } catch (exception: Exception) { - Timber.e(exception) - } - delay(delayBetweenRetries) - } - return block() // last attempt } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase8/RoomAndCoroutinesViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase8/RoomAndCoroutinesViewModel.kt index e8f82780..ee96c7fc 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase8/RoomAndCoroutinesViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase8/RoomAndCoroutinesViewModel.kt @@ -1,9 +1,7 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase8 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import kotlinx.coroutines.launch class RoomAndCoroutinesViewModel( private val api: MockApi, @@ -11,35 +9,11 @@ class RoomAndCoroutinesViewModel( ) : BaseViewModel() { fun loadData() { - uiState.value = UiState.Loading.LoadFromDb - viewModelScope.launch { - val localVersions = database.getAndroidVersions() - if (localVersions.isEmpty()) { - uiState.value = - UiState.Error(DataSource.DATABASE, "Database empty!") - } else { - uiState.value = - UiState.Success(DataSource.DATABASE, localVersions.mapToUiModelList()) - } - - uiState.value = UiState.Loading.LoadFromNetwork - try { - val recentVersions = api.getRecentAndroidVersions() - for (version in recentVersions) { - database.insert(version.mapToEntity()) - } - uiState.value = UiState.Success(DataSource.NETWORK, recentVersions) - } catch (exception: Exception) { - uiState.value = UiState.Error(DataSource.NETWORK, "Something went wrong!") - } - } } fun clearDatabase() { - viewModelScope.launch { - database.clear() - } + } } diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase9/DebuggingCoroutinesViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase9/DebuggingCoroutinesViewModel.kt index 004b7a89..0f4a12eb 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase9/DebuggingCoroutinesViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase9/DebuggingCoroutinesViewModel.kt @@ -1,56 +1,13 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase9 -import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.utils.addCoroutineDebugInfo -import kotlinx.coroutines.* -import timber.log.Timber class DebuggingCoroutinesViewModel( private val api: MockApi = mockApi() ) : BaseViewModel() { fun performSingleNetworkRequest() { - uiState.value = UiState.Loading - // This property needs to be set so that the coroutine name is printed when logging Thread.currentName() - // System.setProperty("kotlinx.coroutines.debug", if (BuildConfig.DEBUG) "on" else "off") - // It is set in [CoroutineUsecasesOnAndroidApplication] - - viewModelScope.launch(CoroutineName("Initial Coroutine")) { - Timber.d(addCoroutineDebugInfo("Initial coroutine launched")) - try { - val recentVersions = api.getRecentAndroidVersions() - Timber.d(addCoroutineDebugInfo("Recent Android Versions returned")) - uiState.value = UiState.Success(recentVersions) - } catch (exception: Exception) { - Timber.d(addCoroutineDebugInfo("Loading recent Android Versions failed")) - uiState.value = UiState.Error("Network Request failed") - } - - // Perform two calculations in parallel - val calculation1Deferred = - async(CoroutineName("Calculation1")) { performCalculation1() } - val calculation2Deferred = - async(CoroutineName("Calculation2")) { performCalculation2() } - - Timber.d(addCoroutineDebugInfo("Result of Calculation1: ${calculation1Deferred.await()}")) - Timber.d(addCoroutineDebugInfo("Result of Calculation2: ${calculation2Deferred.await()}")) - } - } - - private suspend fun performCalculation1() = withContext(Dispatchers.Default) { - Timber.d(addCoroutineDebugInfo("Starting Calculation1")) - delay(1000) - Timber.d(addCoroutineDebugInfo("Calculation1 completed")) - 13 - } - - private suspend fun performCalculation2() = withContext(Dispatchers.Default) { - Timber.d(addCoroutineDebugInfo("Starting Calculation2")) - delay(2000) - Timber.d(addCoroutineDebugInfo("Calculation2 completed")) - 42 } } \ No newline at end of file From 337291f1d5a3394bce3e5416e25ccecfefedf5ca Mon Sep 17 00:00:00 2001 From: lukaslechner Date: Wed, 10 Jun 2020 11:07:52 +0200 Subject: [PATCH 02/29] Delete playground examples for branch "coroutines_course_empty" --- .../playground/coroutine_scope.kt | 25 -------------- .../playground/coroutinebuilders/1_launch.kt | 21 ------------ .../playground/coroutinebuilders/2_async.kt | 34 ------------------- .../playground/fundamentals/1_routines.kt | 14 -------- .../playground/fundamentals/2_coroutines.kt | 21 ------------ .../fundamentals/3_routines_threads.kt | 19 ----------- .../4_coroutines_with_thread_info.kt | 21 ------------ .../5_starting_lots_of_coroutines.kt | 14 -------- .../7_starting_lots_of_threads.kt | 12 ------- .../fundamentals/8_suspending_coroutines.kt | 27 --------------- .../9_coroutine_in_different_threads.kt | 20 ----------- 11 files changed, 228 deletions(-) delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutine_scope.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/1_launch.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/2_async.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/1_routines.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/2_coroutines.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/3_routines_threads.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/4_coroutines_with_thread_info.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/5_starting_lots_of_coroutines.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/7_starting_lots_of_threads.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/8_suspending_coroutines.kt delete mode 100644 app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/9_coroutine_in_different_threads.kt diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutine_scope.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutine_scope.kt deleted file mode 100644 index ba990905..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutine_scope.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { // this: CoroutineScope - launch { - delay(200L) - println("Task from runBlocking") - } - - coroutineScope { // Creates a new coroutine scope - launch { - delay(900L) - println("Task from nested launch") - } - - delay(100L) - println("Task from coroutine scope") // This line will be printed before nested launch - } - - println("Coroutine scope is over") // This line is not printed until nested launch completes -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/1_launch.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/1_launch.kt deleted file mode 100644 index 1df4db87..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/1_launch.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.coroutinebuilders - -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - val job = launch(start = CoroutineStart.LAZY) { - networkRequest() - println("result received") - } - delay(200) - job.start() - println("end of runBlocking") -} - -suspend fun networkRequest(): String { - delay(500) - return "Result" -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/2_async.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/2_async.kt deleted file mode 100644 index 2fa73867..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/coroutinebuilders/2_async.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.coroutinebuilders - -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - - val startTime = System.currentTimeMillis() - - val deferred1 = async { - val result1 = networkCall(1).also { - println("result received: $it after ${elapsedMillis(startTime)}ms") - } - result1 - } - - val deferred2 = async { - val result2 = networkCall(2) - println("result received: $result2 after ${elapsedMillis(startTime)}ms") - result2 - } - - val resultList = listOf(deferred1.await(), deferred2.await()) - - println("Result list: $resultList after ${elapsedMillis(startTime)}ms") -} - -suspend fun networkCall(number: Int): String { - delay(500) - return "Result $number" -} - -fun elapsedMillis(startTime: Long) = System.currentTimeMillis() - startTime diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/1_routines.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/1_routines.kt deleted file mode 100644 index fe83e51c..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/1_routines.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -fun main() { - println("main starts") - routine(1, 500) - routine(2, 300) - println("main ends") -} - -fun routine(number: Int, delay: Long) { - println("Routine $number starts work") - Thread.sleep(delay) - println("Routine $number has finished") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/2_coroutines.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/2_coroutines.kt deleted file mode 100644 index a7c1d0f0..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/2_coroutines.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - println("main starts") - joinAll( - async { coroutine(1, 500) }, - async { coroutine(2, 300) } - ) - println("main ends") -} - -suspend fun coroutine(number: Int, delay: Long) { - println("Coroutine $number starts work") - delay(delay) - println("Coroutine $number has finished") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/3_routines_threads.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/3_routines_threads.kt deleted file mode 100644 index ec1ecd33..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/3_routines_threads.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlin.concurrent.thread - -fun main() { - println("main starts") - threadRoutine(1, 500) - threadRoutine(2, 300) - Thread.sleep(1000) - println("main ends") -} - -fun threadRoutine(number: Int, delay: Long) { - thread { - println("Routine $number starts work") - Thread.sleep(delay) - println("Routine $number has finished") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/4_coroutines_with_thread_info.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/4_coroutines_with_thread_info.kt deleted file mode 100644 index fd712c53..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/4_coroutines_with_thread_info.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - println("main starts") - joinAll( - async { threadInfoCoroutine(1, 500) }, - async { threadInfoCoroutine(2, 300) } - ) - println("main ends") -} - -suspend fun threadInfoCoroutine(number: Int, delay: Long) { - println("Coroutine $number starts work on ${Thread.currentThread().name}") - delay(delay) - println("Coroutine $number has finished on ${Thread.currentThread().name}") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/5_starting_lots_of_coroutines.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/5_starting_lots_of_coroutines.kt deleted file mode 100644 index 5c445957..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/5_starting_lots_of_coroutines.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - repeat(1_000_000) { - launch { - delay(5000) - print(".") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/7_starting_lots_of_threads.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/7_starting_lots_of_threads.kt deleted file mode 100644 index 0ecae8c1..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/7_starting_lots_of_threads.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlin.concurrent.thread - -fun main() { - repeat(1_000_000) { - thread { - Thread.sleep(5000) - print(".") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/8_suspending_coroutines.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/8_suspending_coroutines.kt deleted file mode 100644 index 4ceb0803..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/8_suspending_coroutines.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - println("main starts") - joinAll( - async { suspendingCoroutine(1, 500) }, - async { suspendingCoroutine(2, 300) }, - async { - repeat(5) { - println("other tasks is working on ${Thread.currentThread().name}") - delay(100) - } - } - ) - println("main ends") -} - -suspend fun suspendingCoroutine(number: Int, delay: Long) { - println("Coroutine $number starts work on ${Thread.currentThread().name}") - delay(delay) - println("Coroutine $number has finished on ${Thread.currentThread().name}") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/9_coroutine_in_different_threads.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/9_coroutine_in_different_threads.kt deleted file mode 100644 index 3ac7a3f3..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/9_coroutine_in_different_threads.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import kotlinx.coroutines.* - -fun main() = runBlocking { - println("main starts") - joinAll( - async { threadSwitchingCoroutine(1, 500) }, - async { threadSwitchingCoroutine(2, 300) } - ) - println("main ends") -} - -suspend fun threadSwitchingCoroutine(number: Int, delay: Long) { - println("Coroutine $number starts work on ${Thread.currentThread().name}") - delay(delay) - withContext(Dispatchers.Default) { - println("Coroutine $number has finished on ${Thread.currentThread().name}") - } -} \ No newline at end of file From 8eb10b3578a873a3e799ff29551f10eda3fad767 Mon Sep 17 00:00:00 2001 From: lukaslechner Date: Wed, 15 Jul 2020 14:29:33 +0200 Subject: [PATCH 03/29] Remove dispatcher from constructor of CalculationInBackgroundViewModel.kt --- .../usecase10/CalculationInBackgroundViewModel.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt index 0ec10a56..0b4062a4 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModel.kt @@ -1,12 +1,8 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase10 import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -class CalculationInBackgroundViewModel( - private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default -) : BaseViewModel() { +class CalculationInBackgroundViewModel : BaseViewModel() { fun performCalculation(factorialOf: Int) { From 421ca3d95b7030fee8047561df91ab1b9804f997 Mon Sep 17 00:00:00 2001 From: lukaslechner Date: Tue, 21 Jul 2020 11:43:35 +0200 Subject: [PATCH 04/29] Prepare exercise#4 --- ...CalculationInSeveralCoroutinesViewModel.kt | 22 +++++++++ .../usecase12/FactorialCalculator.kt | 48 +++++++++---------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt index de3b0c15..f2a5f3a7 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/CalculationInSeveralCoroutinesViewModel.kt @@ -3,6 +3,8 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import java.math.BigInteger +import kotlin.system.measureTimeMillis class CalculationInSeveralCoroutinesViewModel( private val factorialCalculator: FactorialCalculator = FactorialCalculator(), @@ -13,6 +15,26 @@ class CalculationInSeveralCoroutinesViewModel( factorialOf: Int, numberOfCoroutines: Int ) { + uiState.value = UiState.Loading + var factorialResult = BigInteger.ZERO + val computationDuration = measureTimeMillis { + factorialResult = + factorialCalculator.calculateFactorial( + factorialOf, + numberOfCoroutines + ) + } + + var resultString = "" + val stringConversionDuration = measureTimeMillis { + resultString = convertToString(factorialResult) + } + + uiState.value = + UiState.Success(resultString, computationDuration, stringConversionDuration) } + + // TODO: execute on background thread + private fun convertToString(number: BigInteger): String = number.toString() } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculator.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculator.kt index 1e621b0d..e4637d2f 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculator.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculator.kt @@ -1,44 +1,41 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase12 -import com.lukaslechner.coroutineusecasesonandroid.utils.addCoroutineDebugInfo -import kotlinx.coroutines.* -import timber.log.Timber +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers import java.math.BigInteger class FactorialCalculator( private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default ) { - suspend fun calculateFactorial( + fun calculateFactorial( factorialOf: Int, numberOfCoroutines: Int ): BigInteger { - return withContext(defaultDispatcher) { - val subRanges = createSubRangeList(factorialOf, numberOfCoroutines) - subRanges.map { subRange -> - async { - calculateFactorialOfSubRange(subRange) - } - }.awaitAll() - .fold(BigInteger.ONE, { acc, element -> - ensureActive() - acc.multiply(element) - }) - } + + // TODO: create sub range list *on background thread* + val subRanges = createSubRangeList(factorialOf, numberOfCoroutines) + + + // TODO: calculate factorial of each subrange in separate coroutine + // use calculateFactorialOfSubRange(subRange) therefore + + + // TODO: create factorial result by multiplying all sub-results and return this + // result + + return BigInteger.ZERO } - suspend fun calculateFactorialOfSubRange( + // TODO: execute on background thread + fun calculateFactorialOfSubRange( subRange: SubRange ): BigInteger { - return withContext(defaultDispatcher) { - Timber.d(addCoroutineDebugInfo("Calculate factorial of $subRange")) - var factorial = BigInteger.ONE - for (i in subRange.start..subRange.end) { - ensureActive() - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - } - factorial + var factorial = BigInteger.ONE + for (i in subRange.start..subRange.end) { + factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) } + return factorial } fun createSubRangeList( @@ -63,4 +60,5 @@ class FactorialCalculator( } } + data class SubRange(val start: Int, val end: Int) \ No newline at end of file From a226736b3c73061f1431b85b6cfdb777a3c09015 Mon Sep 17 00:00:00 2001 From: lukaslechner Date: Tue, 4 Aug 2020 15:21:13 +0200 Subject: [PATCH 05/29] Remove code in AndroidVersionRepository.kt --- .../usecase14/AndroidVersionRepository.kt | 19 ++---------- ...eCoroutineWhenUserLeavesScreenViewModel.kt | 30 ++++++++++++++++++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepository.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepository.kt index 29176147..078d6143 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepository.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepository.kt @@ -3,9 +3,6 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async -import kotlinx.coroutines.launch -import timber.log.Timber class AndroidVersionRepository( private var database: AndroidVersionDao, @@ -18,20 +15,10 @@ class AndroidVersionRepository( } suspend fun loadAndStoreRemoteAndroidVersions(): List { - return scope.async { - val recentVersions = api.getRecentAndroidVersions() - Timber.d("Recent Android versions loaded") - for (recentVersion in recentVersions) { - Timber.d("Insert $recentVersion to database") - database.insert(recentVersion.mapToEntity()) - } - recentVersions - }.await() - } + return emptyList() + } fun clearDatabase() { - scope.launch { - database.clear() - } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt index 3c228f8e..0a4aee7a 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/ContinueCoroutineWhenUserLeavesScreenViewModel.kt @@ -1,17 +1,45 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase14 +import androidx.lifecycle.viewModelScope import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel +import kotlinx.coroutines.launch class ContinueCoroutineWhenUserLeavesScreenViewModel( private var repository: AndroidVersionRepository ) : BaseViewModel() { + // more information in this blogpost about "Coroutines & Patterns for work that shouldn't + // be cancelled" => + // https://medium.com/androiddevelopers/coroutines-patterns-for-work-that-shouldnt-be-cancelled-e26c40f142ad + fun loadData() { + uiState.value = UiState.Loading.LoadFromDb + + viewModelScope.launch { + val localVersions = repository.getLocalAndroidVersions() + if (localVersions.isNotEmpty()) { + uiState.value = + UiState.Success(DataSource.Database, localVersions) + } else { + uiState.value = + UiState.Error(DataSource.Database, "Database empty!") + } + uiState.value = UiState.Loading.LoadFromNetwork + + try { + uiState.value = UiState.Success( + DataSource.Network, + repository.loadAndStoreRemoteAndroidVersions() + ) + } catch (exception: Exception) { + uiState.value = UiState.Error(DataSource.Network, "Network Request failed") + } + } } fun clearDatabase() { - + repository.clearDatabase() } } From 685623e1e0add2cb3cff4552d5568c9ab20c2731 Mon Sep 17 00:00:00 2001 From: lukaslechner Date: Wed, 2 Sep 2020 09:26:11 +0200 Subject: [PATCH 06/29] Remove button in UseCase#13 --- app/src/main/res/layout/activity_exceptionhandling.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/src/main/res/layout/activity_exceptionhandling.xml b/app/src/main/res/layout/activity_exceptionhandling.xml index bc45e05e..3a7f8c8a 100644 --- a/app/src/main/res/layout/activity_exceptionhandling.xml +++ b/app/src/main/res/layout/activity_exceptionhandling.xml @@ -36,14 +36,6 @@ android:layout_marginBottom="16dp" android:text="Show Results even when Child Coroutine Fails (Try/Catch)" /> -