Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Tutorial1-1CoroutinesBasics/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,5 @@ dependencies {
// Espresso
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
}
3 changes: 2 additions & 1 deletion Tutorial1-1CoroutinesBasics/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smarttoolfactory.tutorial1_1basics">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
Expand All @@ -22,6 +22,7 @@
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter1_basics.Activity1Basics" />
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter2_scopes.Activity2CoroutineScope"/>
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter3_lifecycle.Activity3CoroutineLifecycle" />
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter3_lifecycle.Activity3LifecycleScope" />
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter4_supervisorjob.Activity4SupervisorJob" />
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter5_viewmodel.Activity5ViewModelScope" />
<activity android:name="com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter5_viewmodel.Activity5ViewModelRxJava" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.smarttoolfactory.tutorial1_1coroutinesbasics.adapter.ChapterSelection
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter1_basics.Activity1Basics
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter2_scopes.Activity2CoroutineScope
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter3_lifecycle.Activity3CoroutineLifecycle
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter3_lifecycle.Activity3LifecycleScope
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter4_supervisorjob.Activity4SupervisorJob
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter5_viewmodel.Activity5ViewModelRxJava
import com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter5_viewmodel.Activity5ViewModelScope
Expand Down Expand Up @@ -42,6 +43,7 @@ class MainActivity : AppCompatActivity(), BaseAdapter.OnRecyclerViewItemClickLis
activityClassModels.add(ActivityClassModel(Activity1Basics::class.java))
activityClassModels.add(ActivityClassModel(Activity2CoroutineScope::class.java))
activityClassModels.add(ActivityClassModel(Activity3CoroutineLifecycle::class.java))
activityClassModels.add(ActivityClassModel(Activity3LifecycleScope::class.java))
activityClassModels.add(ActivityClassModel(Activity4SupervisorJob::class.java))
activityClassModels.add(ActivityClassModel(Activity5ViewModelScope::class.java))
activityClassModels.add(ActivityClassModel(Activity5ViewModelRxJava::class.java))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.smarttoolfactory.tutorial1_1coroutinesbasics.chapter3_lifecycle

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.smarttoolfactory.tutorial1_1basics.R
import com.smarttoolfactory.tutorial1_1basics.databinding.Activity3LifecycleScopeBinding
import com.smarttoolfactory.tutorial1_1coroutinesbasics.util.dataBinding
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/*
lifecycleScope.launch{} is an alternatie to Handler().postDelayed()

activity.lifecycleScope.launch {
// scope bound to Activity Lifecycle
}
fragment.lifecycleScope.launch {
// scope bound to Fragment Lifecycle
}
fragment.viewLifecycleOwner.launch{
// scope bound to Fragment View
}

Be aware that lifecycleScope is convenient when dealing with UI events like, for example, showing a tip for the user and hiding it after a small delay.
lifecycleScope.launch {
delay(5000)
showTip()
delay(5000)
hideTip()
}

Without using Coroutines and lifecycleScope, this would be:
val DELAY = 5000
Handler().postDelayed({
showTip()
Handler().postDelayed({
hideTip()
}, DELAY)
}, DELAY)

LifecycleScope is bound to Dispatcher.Main. That means if you don’t change Dispatcher explicitly all the coroutines inside LifecycleScope will be executed on the main thread.

https://medium.com/corouteam/exploring-kotlin-coroutines-and-lifecycle-architectural-components-integration-on-android-c63bb8a9156f
https://android.jlelse.eu/coroutine-in-android-working-with-lifecycle-fc9c1a31e5f3
https://kotlin.christmas/2019/13
* */

class Activity3LifecycleScope : AppCompatActivity(R.layout.activity3_lifecycle_scope) {

private val binding: Activity3LifecycleScopeBinding by dataBinding()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val builder = AlertDialog.Builder(this)
builder.setMessage("Mesajj")
builder.setTitle("Titlee")
val alertDialog = builder.create()


binding.button1.setOnClickListener {
binding.textViewResult.text = binding.textViewResult.text.toString() + "Clicked\n"
lifecycleScope.launch {

binding.textViewResult.text =
binding.textViewResult.text.toString() + "🤓 Delay 5 sn before showing dialog\n"
delay(5000)

alertDialog.show()

binding.textViewResult.text =
binding.textViewResult.text.toString() + "🥳 Delay 5 sn after showing dialog\n"
delay(5000)

binding.textViewResult.text =
binding.textViewResult.text.toString() + "Dismissed AlertDialog \n"
Toast.makeText(baseContext, "Dismissed AlertDialog", Toast.LENGTH_SHORT).show()
alertDialog.dismiss()
}
binding.textViewResult.text =
binding.textViewResult.text.toString() + "🚗🚗🚗 Codes after lifecycleScope \n"
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,24 @@ class CoroutinesViewModel(private val viewModelDispatcher: CoroutineDispatcher)

fun getMockResult(timeMillis: Long = 2000) {

println("getMockResult() loading...")
println("1 - getMockResult() loading...")
result.value = "Loading..."
enableResultButton.value = false

viewModelScope.launch {

println("🙄 getMockResult() START ViewModel scope: $this, thread: ${Thread.currentThread().name}")
println("🙄 2 - getMockResult() START ViewModel scope: $this, thread: ${Thread.currentThread().name}")

result.value = generateMockNetworkResponseOrThrowException(timeMillis)
result.value = generateMockNetworkResponseOrThrowException(timeMillis) // 4
enableResultButton.value = true

println("getMockResult() ViewModel scope AFTER generateMockNetworkResponse() ${result.value}")
println("5 - getMockResult() ViewModel scope AFTER generateMockNetworkResponse() ${result.value}")

}

println("getMockResult() END OF FUN")
println("3 - getMockResult() END OF FUN")

/*
/* 1 2 4 3 5
Prints:
I: getMockResult() loading...
I: 🙄 getMockResult() ViewModel scope: StandaloneCoroutine{Active}@ef7c60d, thread: main
Expand All @@ -72,55 +72,45 @@ class CoroutinesViewModel(private val viewModelDispatcher: CoroutineDispatcher)

}


/**
* This method is for Unit-Testing exceptions
/*
Mock Response Functions
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun throwExceptionInAScope(coroutineContext: CoroutineContext) {

println("getMockResult() loading...")

// 🔥🔥
viewModelScope.launch(coroutineContext) {

println("🙄 getMockResult() START ViewModel scope: $this, thread: ${Thread.currentThread().name}")

delay(2000)
throw RuntimeException("Exception Occurred")
private suspend fun generateMockNetworkResponseOrThrowException(timeMillis: Long = 2000): String {

}
println("🥶 4 - generateMockNetworkResponse() thread: ${Thread.currentThread().name}")

println("getMockResult() END OF FUN")
delay(timeMillis)

if (timeMillis > 2000) throw RuntimeException("Threw Network Exception")

return "Hello World"
}


fun getMockResultFromDispatcherThread(timeMillis: Long) {

println("getMockResult() loading...")
println("1 - getMockResult() loading...")
result.value = "Loading..."
enableResultButton.value = false

viewModelScope.launch(Dispatchers.Default) {

println("🙄 getMockResult() ViewModel scope: $this, thread: ${Thread.currentThread().name}")
println("🙄 3 - getMockResult() ViewModel scope: $this, thread: ${Thread.currentThread().name}")

withContext(Dispatchers.Main) {
result.value = generateMockNetworkResponseOrThrowException(timeMillis)
result.value = generateMockNetworkResponseOrThrowException(timeMillis) // 4
enableResultButton.value = true

}

println("getMockResult() ViewModel scope AFTER")
println("5 - getMockResult() ViewModel scope AFTER")

}

println("getMockResult() END OF FUN")
println("2 - getMockResult() END OF FUN")

/*
Prints:
Prints: 1 3 2 4 5 or 1 2 3 4 5

*/

Expand Down Expand Up @@ -155,9 +145,9 @@ class CoroutinesViewModel(private val viewModelDispatcher: CoroutineDispatcher)
// longer than 2000 ms
resultWithTimeout.value = generateMockNetworkResponseOrThrowException()
} catch (exception: TimeoutCancellationException) {
resultWithTimeout.value = exception.message
resultWithTimeout.value = "1 - " + exception.message
} catch (exception: Exception) {
resultWithTimeout.value = exception.message
resultWithTimeout.value = "2 - " + exception.message
}

}
Expand Down Expand Up @@ -283,21 +273,6 @@ class CoroutinesViewModel(private val viewModelDispatcher: CoroutineDispatcher)

}

/*
Mock Response Functions
*/

private suspend fun generateMockNetworkResponseOrThrowException(timeMillis: Long = 2000): String {

println("🥶 generateMockNetworkResponse() thread: ${Thread.currentThread().name}")

delay(timeMillis)

if (timeMillis > 2000) throw RuntimeException("Threw Network Exception")

return "Hello World"
}

private suspend fun generateRandomCity(): String {
val cityList = listOf("Berlin", "New York", "London", "Paris", "Istanbul")

Expand All @@ -317,6 +292,27 @@ class CoroutinesViewModel(private val viewModelDispatcher: CoroutineDispatcher)

}

/**
* This method is for Unit-Testing exceptions
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun throwExceptionInAScope(coroutineContext: CoroutineContext) {

println("getMockResult() loading...")

// 🔥🔥
viewModelScope.launch(coroutineContext) {

println("🙄 getMockResult() START ViewModel scope: $this, thread: ${Thread.currentThread().name}")

delay(2000)
throw RuntimeException("Exception Occurred")

}

println("getMockResult() END OF FUN")

}
}

class CoroutinesViewModelFactory :
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

// This file was automatically generated from channels.md by Knit tool. Do not edit.
package com.smarttoolfactory.tutorial1_1coroutinesbasics.playground

import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

/*
Rendezvous channel example
*/


fun main() = runBlocking {
val channel = Channel<String>()

launch {
println("Sending A1")
channel.send("A1")
println("Sending A2")
channel.send("A2")
println("A done")
}

launch {
println("Sending B1")
channel.send("B1")
println("B done")
}

launch {
repeat(3) {
println("Calling receive()")
val x = channel.receive()
println("receive is done $x")
}
}

println("Done!")
}

/*
* Output :

Done!
Sending A1
Sending B1
Calling receive()
receive is done A1
Calling receive()
receive is done B1
Calling receive()
Sending A2
A done
B done
receive is done A2

* */
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@
package com.smarttoolfactory.tutorial1_1coroutinesbasics.playground

import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
fun main() = runBlocking { // coroutine 1

val channel = Channel<Int>()
launch {
launch { // coroutine 2
// this might be heavy CPU-consuming computation or async logic, we'll just send five squares
for (x in 1..5) channel.send(x * x)
for (x in 1..5) {
println("send $x ")
channel.send(x * x)
}

}
// here we print five received integers:
repeat(5) { println(channel.receive()) }
repeat(5) {
delay(3000)
println(channel.receive())
}
println("Done!")
}

Loading