diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 049ea049..62fad4de 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -19,4 +19,4 @@ jobs: run: ./gradlew test - name: Build Project - run: ./gradlew assemble \ No newline at end of file + run: ./gradlew assemble diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b86273d9..b589d56e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 6c39a4ff..13e76d17 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -11,7 +11,7 @@ - + diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/cancellation/1_cancellation.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/cancellation/1_cancellation.kt deleted file mode 100644 index 0e6e57d4..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/cancellation/1_cancellation.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.cancellation - -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - - val job = launch { - repeat(10) { index -> - println("operation number $index") - try { - delay(100) - } catch (exception: CancellationException) { - println("CancellationException was thrown") - throw CancellationException() - } - } - } - - delay(250) - println("Cancelling Coroutine") - job.cancel() -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/cancellation/2_cooperative_cancellation.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/cancellation/2_cooperative_cancellation.kt deleted file mode 100644 index 78931a57..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/cancellation/2_cooperative_cancellation.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.cancellation - -import kotlinx.coroutines.* - -fun main() = runBlocking { - - val job = launch(Dispatchers.Default) { - repeat(10) { index -> - if (isActive) { - println("operation number $index") - Thread.sleep(100) - } else { - // perform some cleanup on cancellation - withContext(NonCancellable) { - delay(100) - println("Clean up done!") - } - throw CancellationException() - } - } - } - - delay(250) - println("Cancelling Coroutine") - job.cancel() - - val globalCoroutineJob = GlobalScope.launch { - repeat(10) { - println("$it") - delay(100) - } - } - delay(250) - globalCoroutineJob.cancel() - delay(1000) -} \ 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/exceptionhandling/1_try_catch.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/1_try_catch.kt deleted file mode 100644 index 3b13535a..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/1_try_catch.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.exceptionhandling - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch - -fun main() { - - val scope = CoroutineScope(Job()) - try { - scope.launch { - functionThatThrowsIt() - } - } catch (e: Exception) { - println("Caught: $e") - } - - Thread.sleep(100) -} - -fun functionThatThrowsIt() { - throw RuntimeException() -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/2_coroutine_exception_handler.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/2_coroutine_exception_handler.kt deleted file mode 100644 index 0967f338..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/2_coroutine_exception_handler.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.exceptionhandling - -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch - -fun main() { - - val exceptionHandler = CoroutineExceptionHandler { context, exception -> - println("Caught $exception in CoroutineExceptionHandler") - } - - val scope = CoroutineScope(Job()) - - scope.launch { - launch(exceptionHandler) { - functionThatThrowsIt() - } - } - - Thread.sleep(100) -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/3_try_catch_vs_exception_handler.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/3_try_catch_vs_exception_handler.kt deleted file mode 100644 index 961ba9b4..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/3_try_catch_vs_exception_handler.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.exceptionhandling - -import kotlinx.coroutines.* - -fun main() { - - val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> - println("Caught exception: $throwable") - } - - val scope = CoroutineScope(Job()) - - scope.launch(exceptionHandler) { - launch { - println("Starting coroutine 1") - delay(100) - throw RuntimeException() - } - launch { - println("Starting coroutine 2") - delay(3000) - println("Coroutine 2 completed") - } - } - - Thread.sleep(5000) - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/4_launch_and_async.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/4_launch_and_async.kt deleted file mode 100644 index fc613fb5..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/4_launch_and_async.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.exceptionhandling - -import kotlinx.coroutines.* - -fun main() { - - val exceptionHandler = CoroutineExceptionHandler { context, exception -> - println("Caught $exception in CoroutineExceptionHandler") - } - - val scope = CoroutineScope(Job() + exceptionHandler) - - scope.async { - val deferred = async { - delay(200) - throw RuntimeException() - } - } - - Thread.sleep(1000) - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/5_exception_handling_specifics_coroutineScope.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/5_exception_handling_specifics_coroutineScope.kt deleted file mode 100644 index 6551fe7b..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/5_exception_handling_specifics_coroutineScope.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking - - -fun main() = runBlocking() { - - try { - doSomeThingSuspend() - } catch (e: Exception) { - println("Caught $e") - } - -} - -private suspend fun doSomeThingSuspend() { - coroutineScope { - launch { - throw RuntimeException() - } - } -} diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/6_exception_handling_specifics_supervisorScope.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/6_exception_handling_specifics_supervisorScope.kt deleted file mode 100644 index d36fb9bc..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/exceptionhandling/6_exception_handling_specifics_supervisorScope.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.exceptionhandling - -import kotlinx.coroutines.* - -fun main() { - - val ceh = CoroutineExceptionHandler { coroutineContext, throwable -> - println("Caught $throwable in CoroutineExceptionHandler") - } - - val scope = CoroutineScope(Job()) - - scope.launch(ceh) { - try { - supervisorScope { - launch { - println("CEH: ${coroutineContext[CoroutineExceptionHandler]}") - throw RuntimeException() - } - } - } catch (e: Exception) { - println("Caught $e") - } - } - - Thread.sleep(100) -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/1-returning-single-item.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/1-returning-single-item.kt deleted file mode 100644 index 39d1f667..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/1-returning-single-item.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.basics - -import java.math.BigInteger - -fun main() { - val result = calculateFactorialOf(5) - println("Result: $result") -} - -// factorial of n (n!) = 1 * 2 * 3 * 4 * ... * n -private fun calculateFactorialOf(number: Int): BigInteger { - var factorial = BigInteger.ONE - for (i in 1..number) { - Thread.sleep(10) - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - } - return factorial -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/2-return-list.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/2-return-list.kt deleted file mode 100644 index d9c5e31b..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/2-return-list.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.basics - -import com.lukaslechner.coroutineusecasesonandroid.playground.utils.printWithTimePassed -import java.math.BigInteger - -fun main() { - val startTime = System.currentTimeMillis() - calculateFactorialOf(5).forEach { - printWithTimePassed(it,startTime) - } -} - -// factorial of n (n!) = 1 * 2 * 3 * 4 * ... * n -private fun calculateFactorialOf(number: Int): List = buildList { - var factorial = BigInteger.ONE - for (i in 1..number) { - Thread.sleep(10) - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - add(factorial) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/3-sequences.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/3-sequences.kt deleted file mode 100644 index 092d1bc5..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/3-sequences.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.basics - -import com.lukaslechner.coroutineusecasesonandroid.playground.utils.printWithTimePassed -import java.math.BigInteger - -fun main() { - val startTime = System.currentTimeMillis() - calculateFactorialOf(5).forEach { - printWithTimePassed(it,startTime) - } - println("Ready for more work!") -} - -// factorial of n (n!) = 1 * 2 * 3 * 4 * ... * n -private fun calculateFactorialOf(number: Int): Sequence = sequence { - var factorial = BigInteger.ONE - for (i in 1..number) { - Thread.sleep(10) - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - yield(factorial) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/4-flow.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/4-flow.kt deleted file mode 100644 index 05ee970c..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/basics/4-flow.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.basics - -import com.lukaslechner.coroutineusecasesonandroid.playground.utils.printWithTimePassed -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import java.math.BigInteger - -fun main() = runBlocking { - val startTime = System.currentTimeMillis() - - launch { - calculateFactorialOf(5).collect { - printWithTimePassed(it, startTime) - } - } - - println("Ready for more work!") -} - -// factorial of n (n!) = 1 * 2 * 3 * 4 * ... * n -private fun calculateFactorialOf(number: Int): Flow = flow { - var factorial = BigInteger.ONE - for (i in 1..number) { - delay(10) - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - emit(factorial) - } -}.flowOn(Dispatchers.Default) \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/builders/1_basic_flow_builders.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/builders/1_basic_flow_builders.kt deleted file mode 100644 index 6d5b64a5..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/builders/1_basic_flow_builders.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.builders - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.emitAll -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOf - -suspend fun main() { - - val firstFlow = flowOf(1).collect { emittedValue -> - println("firstFlow: $emittedValue") - } - - val secondFlow = flowOf(1, 2, 3) - - secondFlow.collect { emittedValue -> - println("secondFlow: $emittedValue") - } - - listOf("A", "B", "C").asFlow().collect { emittedValue -> - println("asFlow: $emittedValue") - } - - flow { - delay(2000) - emit("item emitted after 2000ms") - - emitAll(secondFlow) - }.collect { emittedValue -> - println("flow{}: $emittedValue") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/1_cancellation_basics.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/1_cancellation_basics.kt deleted file mode 100644 index 6c03e774..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/1_cancellation_basics.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.cancellation - -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch -import kotlin.coroutines.EmptyCoroutineContext - -suspend fun main() { - val scope = CoroutineScope(EmptyCoroutineContext) - - scope.launch { - intFlow() - .onCompletion { throwable -> - if (throwable is CancellationException) { - println("Flow got cancelled.") - } - } - .collect { - println("Collected $it") - - if (it == 2) { - cancel() - } - } - }.join() -} - -private fun intFlow() = flow { - emit(1) - emit(2) - emit(3) -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/2_cooperative_cancellation_in_flows.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/2_cooperative_cancellation_in_flows.kt deleted file mode 100644 index 75c4cdcc..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/2_cooperative_cancellation_in_flows.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.cancellation - -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.onCompletion -import java.math.BigInteger -import kotlin.coroutines.EmptyCoroutineContext - -suspend fun main() { - val scope = CoroutineScope(EmptyCoroutineContext) - - scope.launch { - intFlow() - .onCompletion { throwable -> - if (throwable is CancellationException) { - println("Flow got cancelled.") - } - } - .collect { - println("Collected $it") - - if (it == 2) { - cancel() - } - } - }.join() -} - -private fun intFlow() = flow { - emit(1) - emit(2) - - println("Start calculation") - calculateFactorialOf(1_000) - println("Calculation finished!") - - emit(3) -} - -private suspend fun calculateFactorialOf(number: Int): BigInteger = coroutineScope { - var factorial = BigInteger.ONE - for (i in 1..number) { - factorial = factorial.multiply(BigInteger.valueOf(i.toLong())) - ensureActive() - } - factorial -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/3_cancellable_operator.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/3_cancellable_operator.kt deleted file mode 100644 index cd3194a5..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/cancellation/3_cancellable_operator.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.cancellation - -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.cancellable -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch -import kotlin.coroutines.EmptyCoroutineContext - -suspend fun main() { - val scope = CoroutineScope(EmptyCoroutineContext) - - scope.launch { - flowOf(1, 2, 3) - .onCompletion { throwable -> - if (throwable is CancellationException) { - println("Flow got cancelled.") - } - }.cancellable() - .collect { - println("Collected $it") - - if (it == 2) { - cancel() - } - } - }.join() -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/1_sending_single_value.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/1_sending_single_value.kt deleted file mode 100644 index cef8c7c4..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/1_sending_single_value.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.channels - -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - val deferred = async { - delay(100) // some computation - 10 - } - - launch { - val result = deferred.await() - println(result) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/2_sending_multiple_values.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/2_sending_multiple_values.kt deleted file mode 100644 index f0d9d514..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/2_sending_multiple_values.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.channels - -import kotlinx.coroutines.channels.consumeEach -import kotlinx.coroutines.channels.produce -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - val channel = produce { - println("Sending 10") - send(10) - - println("Sending 20") - send(20) - } - - launch { - channel.consumeEach { receivedValue -> - println("Consumer1: $receivedValue") - } - } - - launch { - channel.consumeEach { receivedValue -> - println("Consumer2: $receivedValue") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/3_drop_while_busy.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/3_drop_while_busy.kt deleted file mode 100644 index 2daa818d..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/channels/3_drop_while_busy.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.channels - -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.channels.consumeEach -import kotlinx.coroutines.channels.produce -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.consumeAsFlow -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.launch - -/** - UseCase: We want to trigger some processing. While the downstream is currently - busy, we want to drop the current trigger emission. - - With a SharedFlow, this is not possible, since we need to define a buffer size of - > 0 for buffer strategies like "DROP_LATEST". Channels however have a buffer sice of 0 - by default. - - Another option is to use the custom operator "dropIfBusy" (see below) - - See also: https://stackoverflow.com/questions/64844821/how-to-drop-latest-with-coroutine-flowt/74560222#74560222 -**/ - -fun Flow.dropIfBusy(): Flow = flow { - coroutineScope { - val channel = produce { - collect { trySend(it) } - } - channel.consumeEach { emit(it) } - } -} - -suspend fun main(): Unit = coroutineScope { - - val channel = Channel() - - launch { - channel - .consumeAsFlow() - .collect { - println("Process $it") - delay(1000) - println("$it processed") - } - } - - launch { - - delay(100) - - // 1 should be processed - channel.trySend(1) - println("sharedFlow emits 1") - - // 2 should not be processed since downstream is busy - channel.trySend(2) - println("sharedFlow emits 2") - - // 3 should be processed again - delay(2000) - channel.trySend(3) - println("sharedFlow emits 3") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/10_buffer_in_stateflow.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/10_buffer_in_stateflow.kt deleted file mode 100644 index 45a01edd..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/10_buffer_in_stateflow.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch -import kotlin.system.measureTimeMillis - -suspend fun main(): Unit = coroutineScope { - - val flow = MutableStateFlow(0) - - // Collector 1 - launch { - flow.collect { - println("Collector 1 processes $it") - } - } - - // Collector 2 - launch { - flow.collect { - println("Collector 2 processes $it") - delay(100) - } - } - - // Emitter - launch { - val timeToEmit = measureTimeMillis { - repeat(5) { - flow.emit(it) - delay(10) - } - } - println("Time to emit all values: $timeToEmit ms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/1_buffer.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/1_buffer.kt deleted file mode 100644 index 6b289951..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/1_buffer.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - println("Emitter: Start Cooking Pancake $it") - delay(100) - println("Emitter: Pancake $it ready!") - emit(it) - } - }.buffer() - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/2_buffer_overflow_suspend.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/2_buffer_overflow_suspend.kt deleted file mode 100644 index acf09b91..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/2_buffer_overflow_suspend.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - val pancakeIndex = it + 1 - println("Emitter: Start Cooking Pancake $pancakeIndex") - delay(100) - println("Emitter: Pancake $pancakeIndex ready!") - emit(pancakeIndex) - } - }.buffer(capacity = 1, onBufferOverflow = BufferOverflow.SUSPEND) - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/3_buffer_overflow_drop_oldest.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/3_buffer_overflow_drop_oldest.kt deleted file mode 100644 index 4ba6dc9a..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/3_buffer_overflow_drop_oldest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - val pancakeIndex = it + 1 - println("Emitter: Start Cooking Pancake $pancakeIndex") - delay(100) - println("Emitter: Pancake $pancakeIndex ready!") - emit(pancakeIndex) - } - }.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/4_buffer_overflow_drop_latest.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/4_buffer_overflow_drop_latest.kt deleted file mode 100644 index 1a1e0bb2..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/4_buffer_overflow_drop_latest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - val pancakeIndex = it + 1 - println("Emitter: Start Cooking Pancake $pancakeIndex") - delay(100) - println("Emitter: Pancake $pancakeIndex ready!") - emit(pancakeIndex) - } - }.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST) - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/5_buffer_unlimited_size.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/5_buffer_unlimited_size.kt deleted file mode 100644 index eed81320..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/5_buffer_unlimited_size.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.buffer -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - val pancakeIndex = it + 1 - println("Emitter: Start Cooking Pancake $pancakeIndex") - delay(100) - println("Emitter: Pancake $pancakeIndex ready!") - emit(pancakeIndex) - } - }.buffer(capacity = UNLIMITED) - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/6_collect_latest.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/6_collect_latest.kt deleted file mode 100644 index abbd0424..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/6_collect_latest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - val pancakeIndex = it + 1 - println("Emitter: Start Cooking Pancake $pancakeIndex") - delay(100) - println("Emitter: Pancake $pancakeIndex ready!") - emit(pancakeIndex) - } - } - - flow.collectLatest { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/7_map_latest.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/7_map_latest.kt deleted file mode 100644 index 4a73326c..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/7_map_latest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.mapLatest - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - val pancakeIndex = it + 1 - println("Emitter: Start Cooking Pancake $pancakeIndex") - delay(100) - println("Emitter: Pancake $pancakeIndex ready!") - emit(pancakeIndex) - } - }.mapLatest { - println("Add topping onto the pancake $it") - delay(200) - it - } - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/8_conflate.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/8_conflate.kt deleted file mode 100644 index d3b4811d..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/8_conflate.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flow - -suspend fun main() = coroutineScope { - - val flow = flow { - repeat(5) { - println("Emitter: Start Cooking Pancake $it") - delay(100) - println("Emitter: Pancake $it ready!") - emit(it) - } - }.conflate() - - flow.collect { - println("Collector: Start eating pancake $it") - delay(300) - println("Collector: Finished eating pancake $it") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/9_buffer_in_sharedflow.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/9_buffer_in_sharedflow.kt deleted file mode 100644 index 7a2a63c0..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/concurrency/9_buffer_in_sharedflow.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.concurrency - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch -import kotlin.system.measureTimeMillis - -suspend fun main(): Unit = coroutineScope { - - val flow = MutableSharedFlow(extraBufferCapacity = 10) - - // Collector 1 - launch { - flow.collect { - println("Collector 1 processes $it") - } - } - - // Collector 2 - launch { - flow.collect { - println("Collector 2 processes $it") - delay(100) - } - } - - // Emitter - launch { - val timeToEmit = measureTimeMillis { - repeat(5) { - flow.emit(it) - delay(10) - } - } - println("Time to emit all values: $timeToEmit ms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/1_try-catch.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/1_try-catch.kt deleted file mode 100644 index 9a03fe22..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/1_try-catch.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - launch { - val stocksFlow = stocksFlow() - .map { - throw Exception("Exception in Map") - } - - try { - stocksFlow - .onCompletion { cause -> - if (cause == null) { - println("Flow completed successfully!") - } else { - println("Flow completed exceptionally with $cause") - } - } - .collect { stock -> - println("Collected $stock") - } - } catch (e: Exception) { - println("Handle Exception in catch block - $e") - } - } -} - -private fun stocksFlow(): Flow = flow { - emit("Apple") - emit("Microsoft") - - throw Exception("Network Request Failed!") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/2_catch.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/2_catch.kt deleted file mode 100644 index 9f6487f9..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/2_catch.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - launch { - val stocksFlow = stocksFlow() - - stocksFlow - .onCompletion { cause -> - if (cause == null) { - println("Flow completed successfully!") - } else { - println("Flow completed exceptionally with $cause") - } - } - .catch { throwable -> - println("Handle exception in catch() operator $throwable") - } - .collect { stock -> - println("Collected $stock") - } - } -} - -private fun stocksFlow(): Flow = flow { - emit("Apple") - emit("Microsoft") - - throw Exception("Network Request Failed!") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/3_emit_single_value.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/3_emit_single_value.kt deleted file mode 100644 index c1db8d7d..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/3_emit_single_value.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - launch { - val stocksFlow = stocksFlow() - - stocksFlow - .onCompletion { cause -> - if (cause == null) { - println("Flow completed successfully!") - } else { - println("Flow completed exceptionally with $cause") - } - } - .catch { throwable -> - println("Handle exception in catch() operator $throwable") - emit("Default Stock") - } - .collect { stock -> - println("Collected $stock") - } - } -} - -private fun stocksFlow(): Flow = flow { - emit("Apple") - emit("Microsoft") - - throw Exception("Network Request Failed!") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/4_fallback_flow.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/4_fallback_flow.kt deleted file mode 100644 index a52980de..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/4_fallback_flow.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - launch { - val stocksFlow = stocksFlow() - - stocksFlow - .onCompletion { cause -> - if (cause == null) { - println("Flow completed successfully!") - } else { - println("Flow completed exceptionally with $cause") - } - } - .catch { throwable -> - println("Handle exception in catch() operator $throwable") - emitAll(fallbackFlow()) - }.catch { throwable -> - println("Handle exception in 2. catch() operator $throwable") - } - .collect { stock -> - println("Collected $stock") - } - } -} - -private fun stocksFlow(): Flow = flow { - emit("Apple") - emit("Microsoft") - - throw Exception("Network Request Failed!") -} - -private fun fallbackFlow(): Flow = flow { - emit("Fallback Stock") - - throw Exception("Exception in Fallback Flow") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/5_exception_in_collect.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/5_exception_in_collect.kt deleted file mode 100644 index e2a88a40..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/5_exception_in_collect.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.* - -suspend fun main(): Unit = coroutineScope { - - val stocksFlow = stocksFlow() - - stocksFlow - .onCompletion { cause -> - if (cause == null) { - println("Flow completed successfully!") - } else { - println("Flow completed exceptionally with $cause") - } - } - .onEach { stock -> - throw Exception("Exception in collect{}") - println("Collected $stock") - }.catch { throwable -> - println("Handle exception in catch() operator $throwable") - } - .launchIn(this) -} - -private fun stocksFlow(): Flow = flow { - emit("Apple") - emit("Microsoft") - - throw Exception("Network Request Failed!") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/6_exception_transparency.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/6_exception_transparency.kt deleted file mode 100644 index 9a61479e..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/6_exception_transparency.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.flow - -suspend fun main(): Unit = coroutineScope { - - flow { - try { - emit(1) - } catch (e: Exception) { - println("Catch exception in flow builder.") - } - }.collect { emittedValue -> - throw Exception("Exception in collect{}") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/7_inlining.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/7_inlining.kt deleted file mode 100644 index 7cce932f..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/7_inlining.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.flow - -suspend fun main(): Unit = coroutineScope { - - flow { - emit(1) - emit(2) - emit(3) - }.collect { - println("Collected $it") - } -} - -val inlinedFlow = flow { - println("Collected 1") - println("Collected 2") - println("Collected 3") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/8_retry.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/8_retry.kt deleted file mode 100644 index b399911b..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/exceptionhandling/8_retry.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.exceptionhandling - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.retryWhen -import kotlinx.coroutines.launch - -suspend fun main(): Unit = coroutineScope { - - launch { - stocksFlow() - .catch { throwable -> - println("Handle exception in catch() operator $throwable") - } - .collect { stockData -> - println("Collected $stockData") - } - } -} - -private fun stocksFlow(): Flow = flow { - - repeat(5) { index -> - - delay(1000) // Network call - - if (index < 4) { - emit("New Stock data") - } else { - throw NetworkException("Network Request Failed!") - } - } -}.retryWhen { cause, attempt -> - println("Enter retry() with $cause") - delay(1000 * (attempt + 1)) - cause is NetworkException -} - -class NetworkException(message: String) : Exception(message) \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/hot_and_cold_flows/1-flows_are_cold.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/hot_and_cold_flows/1-flows_are_cold.kt deleted file mode 100644 index ef99af1f..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/hot_and_cold_flows/1-flows_are_cold.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.hot_and_cold_flows - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.launch - -fun coldFlow() = flow { - println("Emitting 1") - emit(1) - - delay(1000) - println("Emitting 2") - emit(2) - - delay(1000) - println("Emitting 3") - emit(3) -} - -suspend fun main(): Unit = coroutineScope { - - launch { - coldFlow() - .collect { - println("Collector 1 collects: $it") - } - } - - launch { - coldFlow() - .collect { - println("Collector 2 collects: $it") - } - } -} diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/hot_and_cold_flows/2_sharedflows_are_hot.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/hot_and_cold_flows/2_sharedflows_are_hot.kt deleted file mode 100644 index a39027f5..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/hot_and_cold_flows/2_sharedflows_are_hot.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.hot_and_cold_flows - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch - -fun main() { - - val sharedFlow = MutableSharedFlow() - - val scope = CoroutineScope(Dispatchers.Default) - - scope.launch { - repeat(5) { - println("SharedFlow emits $it") - sharedFlow.emit(it) - delay(200) - } - } - - scope.launch { - sharedFlow.collect{ - println("Collected $it in collector 1") - } - } - - scope.launch { - sharedFlow.collect{ - println("Collected $it in collector 2") - } - } - - Thread.sleep(1500) -} diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/1_map.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/1_map.kt deleted file mode 100644 index f11c94e3..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/1_map.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.mapNotNull - -suspend fun main() { - - flowOf(1, 2, 3, 4, 5) - .mapNotNull { "Emission $it" } - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/2_filter.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/2_filter.kt deleted file mode 100644 index 7ce51e11..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/2_filter.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.flowOf - -suspend fun main() { - - flowOf(1, 2, 3, 4, 5) - .filterIsInstance() - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/3_take.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/3_take.kt deleted file mode 100644 index f84ff419..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/3_take.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.takeWhile - -suspend fun main() { - - flowOf(1, 2, 3, 4, 5) - .takeWhile { it < 3 } - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/4_drop.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/4_drop.kt deleted file mode 100644 index 1bb3c389..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/4_drop.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.dropWhile -import kotlinx.coroutines.flow.flowOf - -suspend fun main() { - - flowOf(1, 2, 3, 4, 5, 1) - .dropWhile { it < 2 } - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/5_transform.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/5_transform.kt deleted file mode 100644 index 5f8e68e6..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/5_transform.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.transform - -suspend fun main() { - - flowOf(1, 2, 3, 4, 5, 1) - .transform { - emit(it) - emit(it * 10) - } - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/6_withIndex.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/6_withIndex.kt deleted file mode 100644 index 07211da7..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/6_withIndex.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.withIndex - -suspend fun main() { - - flowOf(1, 2, 3, 4, 5, 1) - .withIndex() - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/7_distinctUntilChanged.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/7_distinctUntilChanged.kt deleted file mode 100644 index c3fc74b3..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/intermediate_operators/7_distinctUntilChanged.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.intermediate_operators - -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flowOf - -suspend fun main() { - - flowOf(1, 1, 2, 3, 4, 5, 1) - .distinctUntilChanged() - .collect { collectedValue -> - println(collectedValue) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/stateflow/1-stateflow.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/stateflow/1-stateflow.kt deleted file mode 100644 index ef5577bb..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/stateflow/1-stateflow.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.stateflow - -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -suspend fun main() { - - val counter = MutableStateFlow(0) - - println(counter.value) - - coroutineScope { - repeat(10_000) { - launch { - counter.update { currentValue -> - currentValue + 1 - } - } - } - } - - println(counter.value) -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/1_flows_vs_lists.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/1_flows_vs_lists.kt deleted file mode 100644 index a5016b60..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/1_flows_vs_lists.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - val list = buildList { - add(1) - println("add 1 to list") - - add(2) - println("add 2 to list") - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/2_collect.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/2_collect.kt deleted file mode 100644 index 3c4bf8ce..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/2_collect.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.runBlocking - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - runBlocking { - flow.collect { receivedValue -> - println("Received value $receivedValue") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/3_first.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/3_first.kt deleted file mode 100644 index 598ae372..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/3_first.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.runBlocking - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - runBlocking { - val item = flow.first { it > 1 } - println("Received $item") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/4_last.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/4_last.kt deleted file mode 100644 index c1700634..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/4_last.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.last -import kotlinx.coroutines.runBlocking - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - runBlocking { - val item = flow.last() - println("Received $item") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/5_single.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/5_single.kt deleted file mode 100644 index a7d6959d..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/5_single.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.single -import kotlinx.coroutines.runBlocking - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - // emit(2) - } - - runBlocking { - val item = flow.single() - println("Received $item") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/6_toList_toSet.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/6_toList_toSet.kt deleted file mode 100644 index d3b54d19..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/6_toList_toSet.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.flow.toSet -import kotlinx.coroutines.runBlocking - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - runBlocking { - val item = flow.toSet() - println("Received $item") - } - - runBlocking { - val item = flow.toList() - println("Received $item") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/7_fold.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/7_fold.kt deleted file mode 100644 index 4159022f..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/7_fold.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.fold -import kotlinx.coroutines.runBlocking - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - runBlocking { - val item = flow.fold(5) { accumulator, emittedItem -> - accumulator + emittedItem - } - println("Received $item") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/8_launchIn.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/8_launchIn.kt deleted file mode 100644 index af6082eb..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/flow/terminal_operators/8_launchIn.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.flow.terminal_operators - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlin.coroutines.EmptyCoroutineContext - -fun main() { - - val flow = flow { - delay(100) - - println("Emitting first value") - emit(1) - - delay(100) - - println("Emitting second value") - emit(2) - } - - val scope = CoroutineScope(EmptyCoroutineContext) - - flow - .onEach { println("Received $it with launchIn() - 1") } - .launchIn(scope) - - flow - .onEach { println("Received $it with launchIn() - 2") } - .launchIn(scope) - - scope.launch { - flow.collect { - println("Received $it in collect - 1") - } - flow.collect { - println("Receive $it in collect - 2") - } - } - - Thread.sleep(1000) -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/10_how_delay_works.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/10_how_delay_works.kt deleted file mode 100644 index 50ea69a1..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/fundamentals/10_how_delay_works.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.fundamentals - -import android.os.Handler -import android.os.Looper -import kotlinx.coroutines.async -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.runBlocking - -fun main() = runBlocking { - println("main starts") - joinAll( - async { delayDemonstration(1, 500) }, - async { delayDemonstration(2, 300) } - ) - println("main ends") -} - -suspend fun delayDemonstration(number: Int, delay: Long) { - println("Coroutine $number starts work") - - // delay(delay) - - Handler(Looper.getMainLooper()) - .postDelayed({ - println("Coroutine $number has finished") - }, 500) -} \ No newline at end of file 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 diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/1_coroutines_need_to_be_started_in_scope.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/1_coroutines_need_to_be_started_in_scope.kt deleted file mode 100644 index 1a26d45e..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/1_coroutines_need_to_be_started_in_scope.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -val scope = CoroutineScope(Dispatchers.Default) - -fun main() = runBlocking { - - val job = scope.launch { - delay(100) - println("Coroutine completed") - } - - job.invokeOnCompletion { throwable -> - if (throwable is CancellationException) { - println("Coroutine was cancelled") - } - } - - delay(50) - onDestroy() -} - -fun onDestroy() { - println("life-time of scope ends") - scope.cancel() -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/2_job_hierarchy.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/2_job_hierarchy.kt deleted file mode 100644 index d4b8dd0e..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/2_job_hierarchy.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -fun main() { - - val scopeJob = Job() - val scope = CoroutineScope(Dispatchers.Default + scopeJob) - - val passedJob = Job() - val coroutineJob = scope.launch(passedJob) { - println("Starting coroutine") - delay(1000) - } - - println("passedJob and coroutineJob are references to the same job object: ${passedJob === coroutineJob}") - - println("Is coroutineJob a child of scopeJob? =>${scopeJob.children.contains(coroutineJob)}") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/3_parents_wait_for_children.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/3_parents_wait_for_children.kt deleted file mode 100644 index 24759e1c..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/3_parents_wait_for_children.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -fun main() = runBlocking { - - val scope = CoroutineScope(Dispatchers.Default) - - val parentCoroutineJob = scope.launch { - launch { - delay(1000) - println("Child Coroutine 1 has completed!") - } - launch { - delay(1000) - println("Child Coroutine 2 has completed!") - } - } - - parentCoroutineJob.join() - println("Parent Coroutine has completed!") -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/4_cancelling_parents_and_children.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/4_cancelling_parents_and_children.kt deleted file mode 100644 index b4cbc7a2..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/4_cancelling_parents_and_children.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -fun main() = runBlocking { - - val scope = CoroutineScope(Dispatchers.Default) - - scope.coroutineContext[Job]!!.invokeOnCompletion { throwable -> - if (throwable is CancellationException) { - println("Parent job was cancelled") - } - } - - val childCoroutine1Job = scope.launch { - delay(1000) - println("Coroutine 1 completed") - } - childCoroutine1Job.invokeOnCompletion { throwable -> - if (throwable is CancellationException) { - println("Coroutine 1 was cancelled!") - } - } - - scope.launch { - delay(1000) - println("Coroutine 2 completed") - }.invokeOnCompletion { throwable -> - if (throwable is CancellationException) { - println("Coroutine 2 was cancelled!") - } - } - - delay(200) - - childCoroutine1Job.cancelAndJoin() - - // scope.coroutineContext[Job]!!.cancelAndJoin() -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/5_exception_propagation.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/5_exception_propagation.kt deleted file mode 100644 index baf323a0..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/5_exception_propagation.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -fun main() { - - val exceptionHandler = CoroutineExceptionHandler { context, exception -> - println("Caught exception $exception") - } - - val scope = CoroutineScope(SupervisorJob() + exceptionHandler) - - scope.launch { - println("Coroutine 1 starts") - delay(50) - println("Coroutine 1 fails") - throw RuntimeException() - } - - scope.launch { - println("Coroutine 2 starts") - delay(500) - println("Coroutine 2 completed") - }.invokeOnCompletion { throwable -> - if (throwable is CancellationException) { - println("Coroutine 2 got cancelled!") - } - } - - Thread.sleep(1000) - - println("Scope got cancelled: ${!scope.isActive}") - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/6_globalscope.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/6_globalscope.kt deleted file mode 100644 index 20d278d9..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/6_globalscope.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -fun main() = runBlocking { - - println("Job of GlobalScope: ${GlobalScope.coroutineContext[Job]}") - - val coroutineExceptionHandler = CoroutineExceptionHandler { context, throwable -> - - } - val job = GlobalScope.launch(coroutineExceptionHandler) { - val child = launch { - delay(50) - throw RuntimeException() - println("Still running") - delay(50) - println("Still running") - delay(50) - println("Still running") - delay(50) - println("Still running") - } - } - - delay(100) - - job.cancel() - - delay(300) - -} \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/7_scoping_functions.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/7_scoping_functions.kt deleted file mode 100644 index a090257c..00000000 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/playground/structuredconcurrency/7_scoping_functions.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.structuredconcurrency - -import kotlinx.coroutines.* - -fun main() { - - val scope = CoroutineScope(Job()) - - scope.launch { - - doSomeTasks() - - launch { - println("Starting Task 3") - delay(300) - println("Task 3 completed") - } - } - - Thread.sleep(1000) -} - -suspend fun doSomeTasks() = coroutineScope { - launch { - println("Starting Task 1") - delay(100) - println("Task 1 completed") - } - - launch { - println("Starting Task 2") - delay(200) - println("Task 2 completed") - } -} \ No newline at end of file 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..0ca79c38 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,16 @@ 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/usecase1/UiState.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/UiState.kt index 4dd9a007..e9f5795f 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/UiState.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/UiState.kt @@ -3,7 +3,7 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion sealed class UiState { - object Loading : UiState() + data object Loading : UiState() data class Success(val recentVersions: List) : UiState() data class Error(val message: String) : UiState() } \ 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..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,52 +1,10 @@ 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() { +class CalculationInBackgroundViewModel : 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..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 @@ -1,11 +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 @@ -19,31 +16,25 @@ class CalculationInSeveralCoroutinesViewModel( 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) - } + var factorialResult = BigInteger.ZERO + val computationDuration = measureTimeMillis { + factorialResult = + factorialCalculator.calculateFactorial( + factorialOf, + numberOfCoroutines + ) + } - uiState.value = - UiState.Success(resultString, computationDuration, stringConversionDuration) + 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() - } + // 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 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/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/usecase2/Perform2SequentialNetworkRequestsViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModel.kt index 2cc394f5..5b77d847 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 @@ -3,6 +3,9 @@ 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.CoroutineScope +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch class Perform2SequentialNetworkRequestsViewModel( @@ -10,19 +13,30 @@ class Perform2SequentialNetworkRequestsViewModel( ) : BaseViewModel() { fun perform2SequentialNetworkRequest() { + uiState.value = UiState.Loading + + // study inside documentation of this later + //also taught later in course viewModelScope.launch { + //just looks like normal fxn call + // AT first it looks like we are synchronously calling 2 functions and blocking the main thread + // but actually that is not the case, main thread is not blocked + //Suspend fxns. when implemented correctly are non-blocking 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") + val featuresOfLatestVersion = mockApi + .getAndroidVersionFeatures( + recentVersions.last().apiLevel + ) + uiState.value = UiState.Success(featuresOfLatestVersion) + } catch (e: Exception) { + uiState.value = UiState.Error("Network request failed") } + // exception hanndling here is also easy } + + + } } \ 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..7be3400c 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,5 +1,6 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.callbacks +import androidx.lifecycle.AndroidViewModel import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures @@ -11,55 +12,68 @@ class SequentialNetworkRequestsCallbacksViewModel( private val mockApi: CallbackMockApi = mockApi() ) : BaseViewModel() { - private var getAndroidVersionsCall: Call>? = null - private var getAndroidFeaturesCall: Call? = null + var getVersions: Call>? = null; + var getFeaturesCall: 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") - } + getVersions = mockApi.getRecentAndroidVersions() + getVersions!!.enqueue( + object : Callback>{ + override fun onResponse( + call: Call>, + response: Response> + ) { + if(response.isSuccessful) { //status code >= 200 and <300 + val latestVersion = response.body()!!.last() + + //Perform Second Call only if first call was successful. - 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") - } + getFeaturesCall = mockApi.getAndroidVersionFeatures(latestVersion.apiLevel) + getFeaturesCall!!.enqueue( + object : Callback { + 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") + } + } - 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 onFailure(call: Call, t: Throwable) { + uiState.value = UiState.Error("Something went wrong") + } + } + + ) + } else { + uiState.value = UiState.Error("Network request failed") + } + } + + override fun onFailure(call: Call>, t: Throwable) { + uiState.value = UiState.Error("Something went wrong") } } - }) + ) + + //since the callbacks are holding the reference to viewModel, it wont get garbage collected when user has left the screen. + } override fun onCleared() { + //this cant access callbacks of prev fxn, so we make them top level. + super.onCleared() - getAndroidVersionsCall?.cancel() - getAndroidFeaturesCall?.cancel() + getVersions?.cancel() + getFeaturesCall?.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..6c117596 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 @@ -14,24 +14,27 @@ class SequentialNetworkRequestsRxViewModel( private val disposables = CompositeDisposable() fun perform2SequentialNetworkRequest() { - uiState.value = UiState.Loading + //use flatMap to chain the two network requests + // .subscribeOn(Schedulers.io()) is used to perform the network requests on a background thread + // .observeOn(AndroidSchedulers.mainThread()) is used to observe the results on the main thread mockApi.getRecentAndroidVersions() - .flatMap { androidVersions -> - val recentVersion = androidVersions.last() - mockApi.getAndroidVersionFeatures(recentVersion.apiLevel) + .flatMap { + val latestVersion = it.last() + mockApi.getAndroidVersionFeatures(latestVersion.apiLevel) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { featureVersions -> - uiState.value = UiState.Success(featureVersions) + .subscribeBy ( + onSuccess = { + uiState.value = UiState.Success(it) }, onError = { - uiState.value = UiState.Error("Network Request failed.") + uiState.value = UiState.Error("Network request failed") } ) .addTo(disposables) + } override fun onCleared() { 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..856a539a 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,10 +1,7 @@ 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() @@ -15,57 +12,10 @@ class TimeoutAndRetryViewModel( val numberOfRetries = 2 val timeout = 1000L - val oreoVersionsDeferred = viewModelScope.async { - retryWithTimeout(numberOfRetries, timeout) { - api.getAndroidVersionFeatures(27) - } - } + // TODO: Exercise 3 + // switch to branch "coroutine_course_full" to see solution - val pieVersionsDeferred = viewModelScope.async { - retryWithTimeout(numberOfRetries, timeout) { - api.getAndroidVersionFeatures(28) - } - } + // run api.getAndroidVersionFeatures(27) and api.getAndroidVersionFeatures(28) in parallel - 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 diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModel.kt index 37690b43..e7a1f39e 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModel.kt @@ -1,27 +1,12 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase1 import androidx.lifecycle.LiveData -import androidx.lifecycle.asLiveData import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.flow.onStart -import timber.log.Timber class FlowUseCase1ViewModel( stockPriceDataSource: StockPriceDataSource ) : BaseViewModel() { - val currentStockPriceAsLiveData: LiveData = stockPriceDataSource - .latestStockList - .map { stockList -> - UiState.Success(stockList) as UiState - } - .onStart { - emit(UiState.Loading) - } - .onCompletion { - Timber.tag("Flow").d("Flow has completed.") - } - .asLiveData() + val currentStockPriceAsLiveData: LiveData = TODO() + } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase2/FlowUseCase2ViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase2/FlowUseCase2ViewModel.kt index ad0b3370..a3956347 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase2/FlowUseCase2ViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase2/FlowUseCase2ViewModel.kt @@ -1,11 +1,8 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase2 import androidx.lifecycle.LiveData -import androidx.lifecycle.asLiveData import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.* -import timber.log.Timber class FlowUseCase2ViewModel( stockPriceDataSource: StockPriceDataSource, @@ -26,48 +23,5 @@ class FlowUseCase2ViewModel( */ - val currentStockPriceAsLiveData: LiveData = stockPriceDataSource - .latestStockList - .withIndex() - .onEach { indexedValue -> - Timber.d("Processing emission ${indexedValue.index + 1}") - } - .map { indexedValue -> - indexedValue.value - } - .take(10) - .filter { stockList -> - val googlePrice = stockList.find { stock -> - stock.name == "Alphabet (Google)" - }?.currentPrice ?: return@filter false - - googlePrice > 2300 - } - .map { stockList -> - stockList.filter { stock -> - stock.country == "United States" - } - } - .map { stockList -> - stockList.filter { stock -> - stock.name != "Apple" && stock.name != "Microsoft" - } - } - .map { stockList -> - stockList.mapIndexed { index, stock -> - stock.copy(rank = index + 1) - } - } - .map { stockList -> - stockList.filter { stock -> - stock.rank <= 10 - } - } - .map { stockList -> - UiState.Success(stockList) as UiState - } - .onStart { - emit(UiState.Loading) - } - .asLiveData(defaultDispatcher) + val currentStockPriceAsLiveData: LiveData = TODO() } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/FlowUseCase3ViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/FlowUseCase3ViewModel.kt index 371cf055..61965c86 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/FlowUseCase3ViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/FlowUseCase3ViewModel.kt @@ -3,7 +3,6 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase3 import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart @@ -38,9 +37,5 @@ class FlowUseCase3ViewModel( .onCompletion { Timber.tag("Flow").d("Flow has completed.") } - .catch { throwable -> - Timber.tag("Flow").d("Enter catch operator with $throwable") - emit(UiState.Error("something went wrong")) - } .asLiveData() } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/StockPriceDataSource.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/StockPriceDataSource.kt index baa396e8..c28daa02 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/StockPriceDataSource.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase3/StockPriceDataSource.kt @@ -5,8 +5,6 @@ import com.lukaslechner.coroutineusecasesonandroid.usecases.flow.mock.Stock import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.retry -import retrofit2.HttpException import timber.log.Timber interface StockPriceDataSource { @@ -22,15 +20,5 @@ class NetworkStockPriceDataSource(mockApi: FlowMockApi) : StockPriceDataSource { emit(currentStockList) delay(5_000) } - }.retry { cause -> - Timber.tag("Flow").d("Enter retry operator with $cause") - - val shouldRetry = cause is HttpException - - if (shouldRetry) { - delay(5_000) - } - - shouldRetry } } \ No newline at end of file diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4Activity.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4Activity.kt index dea1ad94..6213de43 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4Activity.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4Activity.kt @@ -2,16 +2,12 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase4 import android.os.Bundle import androidx.activity.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import com.lukaslechner.coroutineusecasesonandroid.base.BaseActivity import com.lukaslechner.coroutineusecasesonandroid.base.flowUseCase4Description import com.lukaslechner.coroutineusecasesonandroid.databinding.ActivityFlowUsecase1Binding import com.lukaslechner.coroutineusecasesonandroid.utils.setGone import com.lukaslechner.coroutineusecasesonandroid.utils.setVisible import com.lukaslechner.coroutineusecasesonandroid.utils.toast -import kotlinx.coroutines.launch import org.joda.time.LocalDateTime import org.joda.time.format.DateTimeFormat @@ -29,11 +25,9 @@ class FlowUseCase4Activity : BaseActivity() { setContentView(binding.root) binding.recyclerView.adapter = adapter - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.currentStockPriceAsFlow.collect { uiState -> - render(uiState) - } + viewModel.currentStockPriceAsLiveData.observe(this) { uiState -> + if (uiState != null) { + render(uiState) } } } diff --git a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModel.kt b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModel.kt index 81fbde85..c4a9fc78 100644 --- a/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModel.kt +++ b/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModel.kt @@ -1,24 +1,27 @@ package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase4 -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData import com.lukaslechner.coroutineusecasesonandroid.base.BaseViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onStart import timber.log.Timber class FlowUseCase4ViewModel( stockPriceDataSource: StockPriceDataSource ) : BaseViewModel() { - val currentStockPriceAsFlow: StateFlow = stockPriceDataSource + val currentStockPriceAsLiveData: LiveData = stockPriceDataSource .latestStockList .map { stockList -> UiState.Success(stockList) as UiState } + .onStart { + emit(UiState.Loading) + } .onCompletion { Timber.tag("Flow").d("Flow has completed.") - }.stateIn( - scope = viewModelScope, - initialValue = UiState.Loading, - started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000) - ) + } + .asLiveData() } \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/playground/testing/1_testing_coroutines.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/playground/testing/1_testing_coroutines.kt deleted file mode 100644 index f6e3f47a..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/playground/testing/1_testing_coroutines.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.playground.testing - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.advanceTimeBy -import kotlinx.coroutines.test.currentTime -import kotlinx.coroutines.test.runTest -import org.junit.Test - - -class SystemUnderTest { - - suspend fun functionWithDelay(): Int { - delay(1000) - return 42 - } -} - -fun CoroutineScope.functionThatStartsNewCoroutine() { - launch { - delay(1000) - println("Coroutine completed!") - } -} - -class TestClass { - - @Test - fun `functionWithDelay should return 42`() = runTest { - - val realTimeStart = System.currentTimeMillis() - val virtualTimeStart = currentTime - - functionThatStartsNewCoroutine() - advanceTimeBy(1000) - - val realTimeDuration = System.currentTimeMillis() - realTimeStart - val virtualTimeDuration = currentTime - virtualTimeStart - - println("Test took $realTimeDuration real ms") - println("Test took $virtualTimeDuration virtual ms") - } - -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/FakeErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/FakeErrorApi.kt deleted file mode 100644 index b82c4495..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/FakeErrorApi.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeErrorApi() : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/FakeSuccessApi.kt deleted file mode 100644 index 06cbc476..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/FakeSuccessApi.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException - -class FakeSuccessApi : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModelTest.kt deleted file mode 100644 index 9a70e9cb..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase1/PerformSingleNetworkRequestViewModelTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase1 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class PerformSingleNetworkRequestViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `should return Success when network request is successful`() = runTest { - val fakeApi = FakeSuccessApi() - val viewModel = PerformSingleNetworkRequestViewModel(fakeApi) - observeViewModel(viewModel) - - assertTrue(receivedUiStates.isEmpty()) - - viewModel.performSingleNetworkRequest() - - assertEquals( - listOf( - UiState.Loading, - UiState.Success(mockAndroidVersions) - ), - receivedUiStates - ) - } - - @Test - fun `should return Error when network request fails`() = runTest { - val fakeApi = FakeErrorApi() - val viewModel = PerformSingleNetworkRequestViewModel(fakeApi) - observeViewModel(viewModel) - - assertTrue(receivedUiStates.isEmpty()) - - viewModel.performSingleNetworkRequest() - - assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed!") - ), - receivedUiStates - ) - } - - private fun observeViewModel(viewModel: PerformSingleNetworkRequestViewModel) { - viewModel.uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModelTest.kt deleted file mode 100644 index d0864dcb..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase10/CalculationInBackgroundViewModelTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase10 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class CalculationInBackgroundViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `performCalculation() should perform correct calculations`() = runTest { - val viewModel = - CalculationInBackgroundViewModel(StandardTestDispatcher(testScheduler)).apply { - observe() - } - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performCalculation(1) - runCurrent() - - Assert.assertEquals( - UiState.Loading, - receivedUiStates.first() - ) - - Assert.assertEquals( - "1", - (receivedUiStates[1] as UiState.Success).result - ) - - receivedUiStates.clear() - - viewModel.performCalculation(2) - runCurrent() - - Assert.assertEquals( - UiState.Loading, - receivedUiStates.first() - ) - - Assert.assertEquals( - "2", - (receivedUiStates[1] as UiState.Success).result - ) - - receivedUiStates.clear() - - viewModel.performCalculation(3) - runCurrent() - - Assert.assertEquals( - UiState.Loading, - receivedUiStates.first() - ) - - Assert.assertEquals( - "6", - (receivedUiStates[1] as UiState.Success).result - ) - - } - - private fun CalculationInBackgroundViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculatorTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculatorTest.kt deleted file mode 100644 index 756fe7d5..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase12/FactorialCalculatorTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase12 - -import junit.framework.Assert.assertEquals -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import org.junit.Test -import java.math.BigInteger - -@ExperimentalCoroutinesApi -class FactorialCalculatorTest { - - @Test - fun `createSubRangeList() should create correct range lists`() { - val testDispatcher = UnconfinedTestDispatcher() - val factorialCalculator = FactorialCalculator(testDispatcher) - - assertEquals( - listOf( - SubRange(1, 3), - SubRange(4, 6), - SubRange(7, 9) - ), - factorialCalculator.createSubRangeList(9, 3) - ) - - assertEquals( - listOf( - SubRange(1, 3), - SubRange(4, 6), - SubRange(7, 10) - ), - factorialCalculator.createSubRangeList(10, 3) - ) - - assertEquals( - listOf( - SubRange(1, 3), - SubRange(4, 6), - SubRange(7, 11) - ), - factorialCalculator.createSubRangeList(11, 3) - ) - } - - @Test - fun calculateFactorialOfSubRange() = runBlocking { - val testDispatcher = UnconfinedTestDispatcher() - val factorialCalculator = FactorialCalculator(testDispatcher) - - assertEquals( - BigInteger.valueOf(6), - factorialCalculator.calculateFactorialOfSubRange(SubRange(1, 3)) - ) - assertEquals( - BigInteger.valueOf(120), - factorialCalculator.calculateFactorialOfSubRange(SubRange(4, 6)) - ) - assertEquals( - BigInteger.valueOf(55440), - factorialCalculator.calculateFactorialOfSubRange(SubRange(7, 11)) - ) - } - - @Test - fun calculateFactorial() = runBlocking { - val testDispatcher = UnconfinedTestDispatcher() - val factorialCalculator = FactorialCalculator(testDispatcher) - - assertEquals( - BigInteger.valueOf(3628800), - factorialCalculator.calculateFactorial(10, 3) - ) - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepositoryTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepositoryTest.kt deleted file mode 100644 index 0ee83fac..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepositoryTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase14 - -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import junit.framework.Assert.assertEquals -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.cancel -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.advanceTimeBy -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Test - -@ExperimentalCoroutinesApi -class AndroidVersionRepositoryTest { - - @Test - fun `getLocalAndroidVersions() should return android versions from database`() = runTest { - val fakeDatabase = FakeDatabase() - - val repository = AndroidVersionRepository(fakeDatabase, this) - assertEquals(mockAndroidVersions, repository.getLocalAndroidVersions()) - } - - @Test - fun `loadRecentAndroidVersions() should return android versions from network`() = runTest { - val fakeDatabase = FakeDatabase() - val fakeApi = FakeApi() - val repository = AndroidVersionRepository( - fakeDatabase, - this, - api = fakeApi - ) - assertEquals(mockAndroidVersions, repository.loadAndStoreRemoteAndroidVersions()) - } - - @Test - fun `loadRecentAndroidVersions() should continue to load and store android versions when calling scope gets cancelled`() = runTest { - val fakeDatabase = FakeDatabase() - val fakeApi = FakeApi() - val applicationScope = this - val repository = AndroidVersionRepository( - fakeDatabase, - applicationScope, - api = fakeApi - ) - - // Sharing the testScheduler with the applicationScope is important! - val viewModelScope = TestScope(this.testScheduler) - val job = viewModelScope.launch { - repository.loadAndStoreRemoteAndroidVersions() - } - - // execute coroutine until delay(1) in the fakeApi - applicationScope.runCurrent() - - // Check if nothing is inserted into the db before we cancel the scope - assertEquals(false, fakeDatabase.insertedIntoDb) - - // Cancel the scope and check if it was indeed cancelled - viewModelScope.cancel() - assertEquals(true, job.isCancelled) - - // continue coroutine execution after delay(1) in the fakeApi - applicationScope.advanceTimeBy(1) - applicationScope.runCurrent() - - assertEquals(true, fakeDatabase.insertedIntoDb) - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/FakeApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/FakeApi.kt deleted file mode 100644 index 9dd28bc2..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/FakeApi.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase14 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay - -class FakeApi : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(1) - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/FakeDatabase.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/FakeDatabase.kt deleted file mode 100644 index 99e1f815..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/FakeDatabase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase14 - -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions - -class FakeDatabase : AndroidVersionDao { - - var insertedIntoDb = false - - override suspend fun getAndroidVersions(): List { - return mockAndroidVersions.mapToEntityList() - } - - override suspend fun insert(androidVersionEntity: AndroidVersionEntity) { - insertedIntoDb = true - } - - override suspend fun clear() {} - -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeFeaturesErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeFeaturesErrorApi.kt deleted file mode 100644 index e0c4c13a..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeFeaturesErrorApi.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeFeaturesErrorApi : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeSuccessApi.kt deleted file mode 100644 index 936f8956..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeSuccessApi.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* - -class FakeSuccessApi : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeVersionsErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeVersionsErrorApi.kt deleted file mode 100644 index 10e07482..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/FakeVersionsErrorApi.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeVersionsErrorApi : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModelTest.kt deleted file mode 100644 index 476302e4..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/Perform2SequentialNetworkRequestsViewModelTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesAndroid10 -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class Perform2SequentialNetworkRequestsViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `should return Success when both network requests are successful`() = runTest { - val fakeApi = FakeSuccessApi() - val viewModel = Perform2SequentialNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.perform2SequentialNetworkRequest() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success(mockVersionFeaturesAndroid10) - ), - receivedUiStates - ) - } - - @Test - fun `should return Error when first network requests fails`() = runTest { - val fakeApi = FakeVersionsErrorApi() - val viewModel = Perform2SequentialNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.perform2SequentialNetworkRequest() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - @Test - fun `should return Error when second network requests fails`() = runTest { - val fakeApi = FakeFeaturesErrorApi() - val viewModel = Perform2SequentialNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.perform2SequentialNetworkRequest() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - private fun Perform2SequentialNetworkRequestsViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeFeaturesErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeFeaturesErrorApi.kt deleted file mode 100644 index cd01c334..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeFeaturesErrorApi.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.callbacks - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import retrofit2.Call -import retrofit2.mock.Calls -import java.io.IOException - -class FakeFeaturesErrorApi : CallbackMockApi { - - override fun getRecentAndroidVersions(): Call> { - return Calls.response(mockAndroidVersions) - } - - override fun getAndroidVersionFeatures(apiLevel: Int): Call { - return Calls.failure(IOException()) - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeSuccessApi.kt deleted file mode 100644 index acadabc5..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeSuccessApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.callbacks - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import retrofit2.Call -import retrofit2.mock.Calls - -class FakeSuccessApi : CallbackMockApi { - - override fun getRecentAndroidVersions(): Call> { - return Calls.response(mockAndroidVersions) - } - - override fun getAndroidVersionFeatures(apiLevel: Int): Call { - val featureMocks = when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - return Calls.response(featureMocks) - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeVersionsErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeVersionsErrorApi.kt deleted file mode 100644 index acbd8aff..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/FakeVersionsErrorApi.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.callbacks - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import retrofit2.Call -import retrofit2.mock.Calls -import java.io.IOException - -class FakeVersionsErrorApi : CallbackMockApi { - - override fun getRecentAndroidVersions(): Call> { - return Calls.response(mockAndroidVersions) - } - - override fun getAndroidVersionFeatures(apiLevel: Int): Call { - return Calls.failure(IOException()) - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModelTest.kt deleted file mode 100644 index 4a88f60e..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase2/callbacks/SequentialNetworkRequestsCallbacksViewModelTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase2.callbacks - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesAndroid10 -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -class SequentialNetworkRequestsCallbacksViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - private val receivedUiStates: MutableList = - arrayListOf() - - @Test - fun `should return Success when both network requests are successful`() { - val fakeApi = FakeSuccessApi() - val viewModel = - SequentialNetworkRequestsCallbacksViewModel( - fakeApi - ) - - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.perform2SequentialNetworkRequest() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success(mockVersionFeaturesAndroid10) - ), - receivedUiStates - ) - } - - @Test - fun `should return Error when first network request (recent-android-versions) fails`() { - val fakeApi = FakeVersionsErrorApi() - val viewModel = - SequentialNetworkRequestsCallbacksViewModel( - fakeApi - ) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.perform2SequentialNetworkRequest() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - @Test - fun `should return Error when second network requests (android-version-features) fails`() { - val fakeApi = FakeFeaturesErrorApi() - val viewModel = - SequentialNetworkRequestsCallbacksViewModel( - fakeApi - ) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.perform2SequentialNetworkRequest() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - private fun SequentialNetworkRequestsCallbacksViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } - -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/FakeErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/FakeErrorApi.kt deleted file mode 100644 index b0fbb361..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/FakeErrorApi.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase3 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeErrorApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - throw EndpointShouldNotBeCalledException() - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - delay(responseDelay) - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/FakeSuccessApi.kt deleted file mode 100644 index b1dd16ee..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/FakeSuccessApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase3 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay - -class FakeSuccessApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - throw EndpointShouldNotBeCalledException() - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - delay(responseDelay) - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModelTest.kt deleted file mode 100644 index 07bfd067..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase3/PerformNetworkRequestsConcurrentlyViewModelTest.kt +++ /dev/null @@ -1,129 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase3 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesAndroid10 -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesOreo -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesPie -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.currentTime -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class PerformNetworkRequestsConcurrentlyViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `performNetworkRequestsSequentially should return data after 3 times the response delay`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = PerformNetworkRequestsConcurrentlyViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsSequentially() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success( - listOf( - mockVersionFeaturesOreo, - mockVersionFeaturesPie, - mockVersionFeaturesAndroid10 - ) - ) - ), - receivedUiStates - ) - - // Verify that requests actually got executed sequentially and it took - // 3000ms to receive all data - Assert.assertEquals( - 3000, - currentTime - ) - } - - @Test - fun `performNetworkRequestsConcurrently should return data after the response delay`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = PerformNetworkRequestsConcurrentlyViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsConcurrently() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success( - listOf( - mockVersionFeaturesOreo, - mockVersionFeaturesPie, - mockVersionFeaturesAndroid10 - ) - ) - ), - receivedUiStates - ) - - // Verify that requests actually got executed concurrently within 1000ms - Assert.assertEquals( - 1000, - currentTime - ) - } - - @Test - fun `performNetworkRequestsConcurrently should return Error when network request fails`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeErrorApi(responseDelay) - val viewModel = PerformNetworkRequestsConcurrentlyViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsConcurrently() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - private fun PerformNetworkRequestsConcurrentlyViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeFeaturesErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeFeaturesErrorApi.kt deleted file mode 100644 index 43361ba7..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeFeaturesErrorApi.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase4 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import kotlinx.coroutines.delay -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeFeaturesErrorApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(responseDelay) - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - delay(responseDelay) - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeSuccessApi.kt deleted file mode 100644 index c77d99dc..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeSuccessApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase4 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import kotlinx.coroutines.delay - -class FakeSuccessApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(1000) - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - delay(responseDelay) - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeVersionsErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeVersionsErrorApi.kt deleted file mode 100644 index bcb12301..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/FakeVersionsErrorApi.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase4 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeVersionsErrorApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(responseDelay) - throw throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModelTest.kt deleted file mode 100644 index d21c1fe7..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase4/VariableAmountOfNetworkRequestsViewModelTest.kt +++ /dev/null @@ -1,195 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase4 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesAndroid10 -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesOreo -import com.lukaslechner.coroutineusecasesonandroid.mock.mockVersionFeaturesPie -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.currentTime -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class VariableAmountOfNetworkRequestsViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `performNetworkRequestsSequentially() should return Success UiState on successful network requests after 4000ms`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = VariableAmountOfNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsSequentially() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success( - listOf( - mockVersionFeaturesOreo, - mockVersionFeaturesPie, - mockVersionFeaturesAndroid10 - ) - ) - ), - receivedUiStates - ) - - Assert.assertEquals( - 4000, - currentTime - ) - } - - @Test - fun `performNetworkRequestsSequentially() should return Error UiState on unsuccessful recent-android-versions network request`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeVersionsErrorApi(responseDelay) - val viewModel = VariableAmountOfNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsSequentially() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - @Test - fun `performNetworkRequestsSequentially() should return Error UiState on unsuccessful android-version-features network request`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeFeaturesErrorApi(responseDelay) - val viewModel = VariableAmountOfNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsSequentially() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - @Test - fun `performNetworkRequestsConcurrently() should return Success UiState on successful network requests after 2000ms`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = VariableAmountOfNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsConcurrently() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success( - listOf( - mockVersionFeaturesOreo, - mockVersionFeaturesPie, - mockVersionFeaturesAndroid10 - ) - ) - ), - receivedUiStates - ) - - Assert.assertEquals( - 2000, - currentTime - ) - } - - @Test - fun `performNetworkRequestsConcurrently() should return Error UiState on unsuccessful recent-android-versions network request`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeVersionsErrorApi(responseDelay) - val viewModel = VariableAmountOfNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsConcurrently() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - @Test - fun `performNetworkRequestsConcurrently() should return Error UiState on unsuccessful android-version-features network request`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeFeaturesErrorApi(responseDelay) - val viewModel = VariableAmountOfNetworkRequestsViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequestsConcurrently() - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - } - - private fun VariableAmountOfNetworkRequestsViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/FakeSuccessApi.kt deleted file mode 100644 index 163efd07..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/FakeSuccessApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase5 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import kotlinx.coroutines.delay - -class FakeSuccessApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(1000) - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - delay(responseDelay) - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/FakeVersionsErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/FakeVersionsErrorApi.kt deleted file mode 100644 index deab526a..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/FakeVersionsErrorApi.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase5 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeVersionsErrorApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(responseDelay) - throw throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModelTest.kt deleted file mode 100644 index 295b88d6..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase5/NetworkRequestWithTimeoutViewModelTest.kt +++ /dev/null @@ -1,104 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase5 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class NetworkRequestWithTimeoutViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `performNetworkRequest() should return Success UiState on successful network request within timeout`() = - runTest { - val responseDelay = 1000L - val timeout = 1001L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = NetworkRequestWithTimeoutViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequest(timeout) - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success(mockAndroidVersions) - ), - receivedUiStates - ) - } - - @Test - fun `performNetworkRequest() should return Error UiState with timeout error message if timeout gets exceeded`() = - runTest { - val responseDelay = 1000L - val timeout = 999L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = NetworkRequestWithTimeoutViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequest(timeout) - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request timed out!") - ), - receivedUiStates - ) - } - - @Test - fun `performNetworkRequest() should return Error UiState on unsuccessful network response`() = - runTest { - val responseDelay = 1000L - val timeout = 1001L - val fakeApi = FakeVersionsErrorApi(responseDelay) - val viewModel = NetworkRequestWithTimeoutViewModel(fakeApi) - viewModel.observe() - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequest(timeout) - - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed!") - ), - receivedUiStates - ) - } - - private fun NetworkRequestWithTimeoutViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeSuccessApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeSuccessApi.kt deleted file mode 100644 index 24d85e4e..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeSuccessApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase6 - -import com.lukaslechner.coroutineusecasesonandroid.mock.* -import kotlinx.coroutines.delay - -class FakeSuccessApi(private val responseDelay: Long) : MockApi { - - override suspend fun getRecentAndroidVersions(): List { - delay(1000) - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - delay(responseDelay) - return when (apiLevel) { - 27 -> mockVersionFeaturesOreo - 28 -> mockVersionFeaturesPie - 29 -> mockVersionFeaturesAndroid10 - else -> throw IllegalArgumentException("apiLevel not found") - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeSuccessOnThirdAttemptApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeSuccessOnThirdAttemptApi.kt deleted file mode 100644 index 74c91038..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeSuccessOnThirdAttemptApi.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase6 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay -import java.io.IOException - -class FakeSuccessOnThirdAttemptApi(private val responseDelay: Long) : MockApi { - - var requestCount = 0 - - override suspend fun getRecentAndroidVersions(): List { - requestCount++ - delay(responseDelay) - if (requestCount < 3) { - throw IOException() - } - return mockAndroidVersions - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeVersionsErrorApi.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeVersionsErrorApi.kt deleted file mode 100644 index 704d0199..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/FakeVersionsErrorApi.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase6 - -import com.lukaslechner.coroutineusecasesonandroid.mock.AndroidVersion -import com.lukaslechner.coroutineusecasesonandroid.mock.MockApi -import com.lukaslechner.coroutineusecasesonandroid.mock.VersionFeatures -import com.lukaslechner.coroutineusecasesonandroid.utils.EndpointShouldNotBeCalledException -import kotlinx.coroutines.delay -import okhttp3.MediaType -import okhttp3.ResponseBody -import retrofit2.HttpException -import retrofit2.Response - -class FakeVersionsErrorApi(private val responseDelay: Long) : MockApi { - - var requestCount = 0 - - override suspend fun getRecentAndroidVersions(): List { - requestCount++ - delay(responseDelay) - throw throw HttpException( - Response.error>( - 500, - ResponseBody.create(MediaType.parse("application/json"), "") - ) - ) - } - - override suspend fun getAndroidVersionFeatures(apiLevel: Int): VersionFeatures { - throw EndpointShouldNotBeCalledException() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModelTest.kt deleted file mode 100644 index e15e1a36..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase6/RetryNetworkRequestViewModelTest.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.coroutines.usecase6 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.mock.mockAndroidVersions -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.currentTime -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -@ExperimentalCoroutinesApi -class RetryNetworkRequestViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `performSingleNetworkRequest() should return Success UiState on successful network response`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeSuccessApi(responseDelay) - val viewModel = RetryNetworkRequestViewModel(fakeApi).apply { - observe() - } - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequest() - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success(mockAndroidVersions) - ), - receivedUiStates - ) - } - - @Test - fun `performSingleNetworkRequest() should retry network request two times`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeSuccessOnThirdAttemptApi(responseDelay) - val viewModel = RetryNetworkRequestViewModel(fakeApi).apply { - observe() - } - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequest() - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Success(mockAndroidVersions) - ), - receivedUiStates - ) - - Assert.assertEquals( - 3, - fakeApi.requestCount - ) - - // 3*1000 (Request delays) + 100 (initial delay) + 200 (second delay) - Assert.assertEquals( - 3300, - currentTime - ) - } - - @Test - fun `performSingleNetworkRequest() should return Error UiState on 3 unsuccessful network responses`() = - runTest { - val responseDelay = 1000L - val fakeApi = FakeVersionsErrorApi(responseDelay) - val viewModel = RetryNetworkRequestViewModel(fakeApi).apply { - observe() - } - - Assert.assertTrue(receivedUiStates.isEmpty()) - - viewModel.performNetworkRequest() - advanceUntilIdle() - - Assert.assertEquals( - listOf( - UiState.Loading, - UiState.Error("Network Request failed") - ), - receivedUiStates - ) - - Assert.assertEquals( - 3, - fakeApi.requestCount - ) - - // 3*1000 response delays + 100 (initial delay) + 200 (second delay) - Assert.assertEquals( - 3300, - currentTime - ) - } - - private fun RetryNetworkRequestViewModel.observe() { - uiState().observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FakeStockPriceDataSource.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FakeStockPriceDataSource.kt deleted file mode 100644 index 5bcbbac9..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FakeStockPriceDataSource.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase1 - -import com.lukaslechner.coroutineusecasesonandroid.usecases.flow.mock.Stock -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow - -class FakeStockPriceDataSource : StockPriceDataSource { - - private val sharedFlow = MutableSharedFlow>() - - suspend fun emit(stockList: List) { - sharedFlow.emit(stockList) - } - - override val latestStockList: Flow> = sharedFlow.asSharedFlow() - -} diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModelTest.kt deleted file mode 100644 index 8bf71c4c..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/FlowUseCase1ViewModelTest.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase1 - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import junit.framework.Assert.assertEquals -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestRule - -class FlowUseCase1ViewModelTest { - - @get:Rule - val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule() - - @OptIn(ExperimentalCoroutinesApi::class) - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - private val receivedUiStates = mutableListOf() - - @Test - fun `should return success UI States with values of fakeDataSource`() = runTest { - - val fakeDataSource = FakeStockPriceDataSource() - val viewModel = FlowUseCase1ViewModel(fakeDataSource) - observeViewModel(viewModel) - - assertEquals(UiState.Loading, receivedUiStates[0]) - assertEquals(1, receivedUiStates.size) - - fakeDataSource.emit(listOf(googleStock, appleStock)) - assertEquals(UiState.Success(listOf(googleStock, appleStock)), receivedUiStates[1]) - assertEquals(2, receivedUiStates.size) - - fakeDataSource.emit(listOf(googleStock)) - assertEquals(UiState.Success(listOf(googleStock)), receivedUiStates[2]) - assertEquals(3, receivedUiStates.size) - } - - private fun observeViewModel(viewModel: FlowUseCase1ViewModel) { - viewModel.currentStockPriceAsLiveData.observeForever { uiState -> - if (uiState != null) { - receivedUiStates.add(uiState) - } - } - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/TestData.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/TestData.kt deleted file mode 100644 index 32e2a66f..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase1/TestData.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase1 - -import com.lukaslechner.coroutineusecasesonandroid.usecases.flow.mock.Stock - -val appleStock = Stock( - rank = 1, - name = "Apple", - symbol = "AAPL", - marketCap = 1.0f, - country = "United Stated", - currentPrice = 1.0f -) - -val googleStock = Stock( - rank = 4, - name = "Alphabet", - symbol = "GOOG", - marketCap = 2.0f, - country = "United Stated", - currentPrice = 2.0f -) diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FakeStockPriceDataSource.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FakeStockPriceDataSource.kt deleted file mode 100644 index 32c11197..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FakeStockPriceDataSource.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase4 - -import com.lukaslechner.coroutineusecasesonandroid.usecases.flow.mock.Stock -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow - -class FakeStockPriceDataSource : StockPriceDataSource { - - private val sharedFlow = MutableSharedFlow>() - - suspend fun emit(stockList: List) { - sharedFlow.emit(stockList) - } - - override val latestStockList: Flow> = sharedFlow.asSharedFlow() - -} diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModelTest.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModelTest.kt deleted file mode 100644 index 6837cef9..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/usecases/flow/usecase4/FlowUseCase4ViewModelTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase4 - -import com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase1.appleStock -import com.lukaslechner.coroutineusecasesonandroid.usecases.flow.usecase1.googleStock -import com.lukaslechner.coroutineusecasesonandroid.utils.ReplaceMainDispatcherRule -import junit.framework.Assert.assertEquals -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test - -class FlowUseCase4ViewModelTest { - - @OptIn(ExperimentalCoroutinesApi::class) - @get: Rule - val replaceMainDispatcherRule = ReplaceMainDispatcherRule() - - @Test - fun `should collect loading and success ui states on successful emissions`() = runTest { - - val fakeStockPriceDataSource = FakeStockPriceDataSource() - val viewModel = FlowUseCase4ViewModel(fakeStockPriceDataSource) - - val collectJob = - launch(UnconfinedTestDispatcher()) { - viewModel.currentStockPriceAsFlow.collect() - } - - assertEquals( - UiState.Loading, - viewModel.currentStockPriceAsFlow.value - ) - - fakeStockPriceDataSource.emit( - listOf( - googleStock, - appleStock - ) - ) - - assertEquals( - UiState.Success( - listOf( - googleStock, - appleStock - ) - ), - viewModel.currentStockPriceAsFlow.value - ) - - fakeStockPriceDataSource.emit(listOf(googleStock)) - - assertEquals( - UiState.Success( - listOf( - googleStock - ) - ), - viewModel.currentStockPriceAsFlow.value - ) - - collectJob.cancel() - } -} \ No newline at end of file diff --git a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/utils/EndpointShouldNotBeCalledException.kt b/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/utils/EndpointShouldNotBeCalledException.kt deleted file mode 100644 index f6c60b0a..00000000 --- a/app/src/test/java/com/lukaslechner/coroutineusecasesonandroid/utils/EndpointShouldNotBeCalledException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.lukaslechner.coroutineusecasesonandroid.utils - -class EndpointShouldNotBeCalledException : Throwable() \ No newline at end of file