├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── rengwuxian
│ │ └── coursecoroutines
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── rengwuxian
│ │ │ └── coursecoroutines
│ │ │ ├── MainActivity.kt
│ │ │ ├── _1_basics
│ │ │ ├── 0_preview.kt
│ │ │ ├── 10_parallel.kt
│ │ │ ├── 11_callback_to_suspend.kt
│ │ │ ├── 12_runBlocking.kt
│ │ │ ├── 1_launch.kt
│ │ │ ├── 2_suspend.kt
│ │ │ ├── 3_android.kt
│ │ │ ├── 4_withContext.kt
│ │ │ ├── 5_custom_suspend.kt
│ │ │ ├── 6_withContext_2.kt
│ │ │ ├── 7_de_magic.kt
│ │ │ └── 9_structured_concurrency.kt
│ │ │ ├── _2_structured_concurrency
│ │ │ ├── 10_structured_exception.kt
│ │ │ ├── 11_async_exception.kt
│ │ │ ├── 12_SupervisorJob.kt
│ │ │ ├── 1_whats_a_coroutine.kt
│ │ │ ├── 2_parent_children.kt
│ │ │ ├── 3_thread_stop.kt
│ │ │ ├── 4_thread_interrupt.kt
│ │ │ ├── 5_cancel.kt
│ │ │ ├── 6_structural_cancellation.kt
│ │ │ ├── 7_NonCancellable.kt
│ │ │ ├── 8_exception.kt
│ │ │ └── 9_CoroutineExceptionHandler.kt
│ │ │ ├── _3_scope_context
│ │ │ ├── 1_scope_context.kt
│ │ │ ├── 2_GlobalScope.kt
│ │ │ ├── 3_coroutineContext.kt
│ │ │ ├── 4_coroutineScope.kt
│ │ │ ├── 5_withContext_again.kt
│ │ │ ├── 6_CoroutineName.kt
│ │ │ ├── 7_CoroutineContext.kt
│ │ │ └── 8_custom_CoroutineContext.kt
│ │ │ ├── _4_flow
│ │ │ ├── 10_filter.kt
│ │ │ ├── 11_distinctUntilChanged.kt
│ │ │ ├── 12_custom_flow.kt
│ │ │ ├── 13_timeout_sample_debounce.kt
│ │ │ ├── 14_drop_take.kt
│ │ │ ├── 15_map.kt
│ │ │ ├── 16_transform.kt
│ │ │ ├── 17_withIndex.kt
│ │ │ ├── 18_reduce_fold.kt
│ │ │ ├── 19_onEach.kt
│ │ │ ├── 1_overview.kt
│ │ │ ├── 20_chunked.kt
│ │ │ ├── 21_try_catch.kt
│ │ │ ├── 22_catch.kt
│ │ │ ├── 23_retry.kt
│ │ │ ├── 24_onStart_onCompletion_onEmpty.kt
│ │ │ ├── 25_flowOn.kt
│ │ │ ├── 26_buffer.kt
│ │ │ ├── 27_merge.kt
│ │ │ ├── 28_convert.kt
│ │ │ ├── 29_SharedFlow.kt
│ │ │ ├── 2_produce.kt
│ │ │ ├── 30_shareIn.kt
│ │ │ ├── 31_MutableSharedFlow.kt
│ │ │ ├── 32_StateFlow.kt
│ │ │ ├── 3_channel.kt
│ │ │ ├── 4_channel_api.kt
│ │ │ ├── 5_actor.kt
│ │ │ ├── 6_flow.kt
│ │ │ ├── 7_flow_use_case.kt
│ │ │ ├── 8_build.kt
│ │ │ └── 9_collect.kt
│ │ │ ├── _5_collaboration
│ │ │ ├── 1_collaboration.kt
│ │ │ ├── 2_select.kt
│ │ │ ├── 3_mutex.kt
│ │ │ ├── 4_ThreadLocal.kt
│ │ │ └── Java.java
│ │ │ ├── common
│ │ │ ├── Api.kt
│ │ │ └── Common.kt
│ │ │ └── ui
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── layout
│ │ ├── layout_1.xml
│ │ └── layout_2.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── com
│ └── rengwuxian
│ └── coursecoroutines
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
└── course-cover.jpg
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 《Kotlin 协程完全教程》配套源码
2 |
3 | 这是课程《Kotlin 协程完全教程 - 从基础实践到进阶再到专家》的配套源码。
4 |
5 | 课程地址:[rengwuxian.com/kc](https://rengwuxian.com/kc)
6 |
7 | 
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.compose.compiler)
5 | }
6 |
7 | android {
8 | namespace = "com.rengwuxian.coursecoroutines"
9 | compileSdk = 34
10 |
11 | defaultConfig {
12 | applicationId = "com.rengwuxian.coursecoroutines"
13 | minSdk = 24
14 | targetSdk = 34
15 | versionCode = 1
16 | versionName = "1.0"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | vectorDrawables {
20 | useSupportLibrary = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | isMinifyEnabled = false
27 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
28 | }
29 | }
30 | compileOptions {
31 | sourceCompatibility = JavaVersion.VERSION_1_8
32 | targetCompatibility = JavaVersion.VERSION_1_8
33 | }
34 | kotlinOptions {
35 | jvmTarget = "1.8"
36 | }
37 | packaging {
38 | resources {
39 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
40 | }
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation(libs.androidx.core.ktx)
46 | implementation(libs.androidx.fragment)
47 | implementation(libs.androidx.lifecycle.runtime.ktx)
48 | implementation(libs.androidx.lifecycle.viewmodel.ktx)
49 | implementation(libs.androidx.activity.compose)
50 | implementation(platform(libs.androidx.compose.bom))
51 | implementation(libs.androidx.ui)
52 | implementation(libs.androidx.ui.graphics)
53 | implementation(libs.androidx.ui.tooling.preview)
54 | implementation(libs.androidx.material3)
55 | implementation(libs.coroutines.core)
56 | implementation(libs.coroutines.android)
57 | implementation(libs.retrofit.lib)
58 | implementation(libs.retrofit.mock)
59 | implementation(libs.retrofit.gson)
60 | implementation(libs.retrofit.rxjava)
61 | implementation(libs.rxjava.core)
62 | implementation(libs.rxjava.android)
63 | testImplementation(libs.junit)
64 | androidTestImplementation(libs.androidx.junit)
65 | androidTestImplementation(libs.androidx.espresso.core)
66 | androidTestImplementation(platform(libs.androidx.compose.bom))
67 | androidTestImplementation(libs.androidx.ui.test.junit4)
68 | debugImplementation(libs.androidx.ui.tooling)
69 | debugImplementation(libs.androidx.ui.test.manifest)
70 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/rengwuxian/coursecoroutines/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.rengwuxian.coursecoroutines", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
36 |
37 |
41 |
42 |
46 |
47 |
51 |
52 |
56 |
57 |
61 |
62 |
66 |
67 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.material3.Scaffold
10 | import androidx.compose.material3.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import com.rengwuxian.coursecoroutines.ui.theme.CourseCoroutinesTheme
15 |
16 | class MainActivity : ComponentActivity() {
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | enableEdgeToEdge()
20 | setContent {
21 | CourseCoroutinesTheme {
22 | Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
23 | Greeting(
24 | name = "Android",
25 | modifier = Modifier.padding(innerPadding)
26 | )
27 | }
28 | }
29 | }
30 | }
31 | }
32 |
33 | @Composable
34 | fun Greeting(name: String, modifier: Modifier = Modifier) {
35 | Text(
36 | text = "Hello $name!",
37 | modifier = modifier
38 | )
39 | }
40 |
41 | @Preview(showBackground = true)
42 | @Composable
43 | fun GreetingPreview() {
44 | CourseCoroutinesTheme {
45 | Greeting("Android")
46 | }
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/0_preview.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import androidx.activity.ComponentActivity
7 | import androidx.activity.compose.setContent
8 | import androidx.activity.enableEdgeToEdge
9 | import androidx.lifecycle.lifecycleScope
10 | import com.rengwuxian.coursecoroutines.common.Contributor
11 | import com.rengwuxian.coursecoroutines.common.gitHub
12 | import com.rengwuxian.coursecoroutines.ui.theme.CourseCoroutinesTheme
13 | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
14 | import io.reactivex.rxjava3.core.Single
15 | import io.reactivex.rxjava3.disposables.Disposable
16 | import kotlinx.coroutines.GlobalScope
17 | import kotlinx.coroutines.async
18 | import kotlinx.coroutines.launch
19 | import retrofit2.Call
20 | import retrofit2.Callback
21 | import retrofit2.Response
22 | import java.util.concurrent.CountDownLatch
23 | import kotlin.concurrent.thread
24 |
25 | class PreviewActivity : ComponentActivity() {
26 | private val handler = Handler(Looper.getMainLooper())
27 |
28 | override fun onCreate(savedInstanceState: Bundle?) {
29 | super.onCreate(savedInstanceState)
30 | enableEdgeToEdge()
31 | setContent {
32 | CourseCoroutinesTheme {}
33 | }
34 |
35 | thread {
36 |
37 | }
38 |
39 | GlobalScope.launch {
40 |
41 | }
42 | }
43 |
44 | private fun callbackStyle() {
45 | gitHub.contributorsCall("square", "retrofit").enqueue(object : Callback> {
46 | override fun onResponse(
47 | call: Call>, response: Response>,
48 | ) {
49 | showContributors(response.body()!!)
50 | }
51 |
52 | override fun onFailure(call: Call>, t: Throwable) {
53 | t.printStackTrace()
54 | }
55 | })
56 | }
57 |
58 | private fun coroutinesStyle() = lifecycleScope.launch {
59 | val contributors = gitHub.contributors("square", "retrofit")
60 | showContributors(contributors)
61 | }
62 |
63 | private fun completableFutureStyleMerge() {
64 | val future1 = gitHub.contributorsFuture("square", "retrofit")
65 | val future2 = gitHub.contributorsFuture("square", "okhttp")
66 | future1.thenCombine(future2) { contributors1, contributors2 ->
67 | contributors1 + contributors2
68 | }.thenAccept { mergedContributors ->
69 | handler.post {
70 | showContributors(mergedContributors)
71 | }
72 | }
73 | }
74 |
75 | private fun rxStyleMerge(): Disposable {
76 | val single1 = gitHub.contributorsRx("square", "retrofit")
77 | val single2 = gitHub.contributorsRx("square", "okhttp")
78 | return Single.zip(single1, single2) { contributors1, contributors2 ->
79 | contributors1 + contributors2
80 | }.observeOn(AndroidSchedulers.mainThread())
81 | .subscribe(::showContributors)
82 | }
83 |
84 | private fun coroutinesStyleMerge() = lifecycleScope.launch {
85 | val contributors1 = async { gitHub.contributors("square", "retrofit") }
86 | val contributors2 = async { gitHub.contributors("square", "okhttp") }
87 | showContributors(contributors1.await() + contributors2.await())
88 | }
89 |
90 | private fun showContributors(contributors: List) = contributors.forEach {
91 | println("${it.login} has made ${it.contributions} contributions")
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/10_parallel.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.widget.TextView
7 | import androidx.activity.ComponentActivity
8 | import androidx.lifecycle.lifecycleScope
9 | import com.rengwuxian.coursecoroutines.R
10 | import com.rengwuxian.coursecoroutines.common.Contributor
11 | import com.rengwuxian.coursecoroutines.common.gitHub
12 | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
13 | import io.reactivex.rxjava3.disposables.Disposable
14 | import kotlinx.coroutines.CoroutineScope
15 | import kotlinx.coroutines.Dispatchers
16 | import kotlinx.coroutines.Job
17 | import kotlinx.coroutines.async
18 | import kotlinx.coroutines.cancel
19 | import kotlinx.coroutines.coroutineScope
20 | import kotlinx.coroutines.delay
21 | import kotlinx.coroutines.launch
22 | import retrofit2.Call
23 | import retrofit2.Callback
24 | import retrofit2.Response
25 | import java.util.concurrent.Executors
26 |
27 | class ParallelActivity : ComponentActivity() {
28 | private val handler = Handler(Looper.getMainLooper())
29 | private lateinit var infoTextView: TextView
30 |
31 | override fun onCreate(savedInstanceState: Bundle?) {
32 | super.onCreate(savedInstanceState)
33 | setContentView(R.layout.layout_1)
34 | infoTextView = findViewById(R.id.infoTextView)
35 |
36 | lifecycleScope.launch {
37 | coroutineScope {
38 | val deferred1 = async { gitHub.contributors("square", "retrofit") }
39 | val deferred2 = async { gitHub.contributors("square", "okhttp") }
40 | showContributors(deferred1.await() + deferred2.await())
41 | }
42 | }
43 | lifecycleScope.launch {
44 | val initJob = launch {
45 | // init()
46 | }
47 | val contributors1 = gitHub.contributors("square", "retrofit")
48 | initJob.join()
49 | // processData()
50 | }
51 | }
52 |
53 | private fun coroutinesStyle() = lifecycleScope.launch {
54 | val contributors1 = gitHub.contributors("square", "retrofit")
55 | val contributors2 = gitHub.contributors("square", "okhttp")
56 | showContributors(contributors1 + contributors2)
57 | }
58 |
59 | private fun completableFutureStyleMerge() {
60 | val future1 = gitHub.contributorsFuture("square", "retrofit")
61 | val future2 = gitHub.contributorsFuture("square", "okhttp")
62 | future1.thenCombine(future2) { contributors1, contributors2 ->
63 | contributors1 + contributors2
64 | }.thenAccept { mergedContributors ->
65 | handler.post {
66 | showContributors(mergedContributors)
67 | }
68 | }
69 | }
70 |
71 | private fun showContributors(contributors: List) = contributors
72 | .map { "${it.login} (${it.contributions})" }
73 | .reduce { acc, s -> "$acc\n$s" }
74 | .let { infoTextView.text = it }
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/11_callback_to_suspend.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.lifecycleScope
7 | import com.rengwuxian.coursecoroutines.R
8 | import com.rengwuxian.coursecoroutines.common.Contributor
9 | import com.rengwuxian.coursecoroutines.common.gitHub
10 | import kotlinx.coroutines.delay
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.suspendCancellableCoroutine
13 | import retrofit2.Call
14 | import retrofit2.Callback
15 | import retrofit2.Response
16 | import kotlin.coroutines.resume
17 | import kotlin.coroutines.resumeWithException
18 | import kotlin.coroutines.suspendCoroutine
19 |
20 | class CallbackToSuspendActivity : ComponentActivity() {
21 | private lateinit var infoTextView: TextView
22 |
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 | setContentView(R.layout.layout_1)
26 | infoTextView = findViewById(R.id.infoTextView)
27 |
28 | val job = lifecycleScope.launch {
29 | try {
30 | val contributors = callbackToCancellableSuspend()
31 | showContributors(contributors)
32 | } catch (e: Exception) {
33 | infoTextView.text = e.message
34 | }
35 | }
36 | //
37 | // lifecycleScope.launch {
38 | // suspendCancellableCoroutine {
39 | //
40 | // }
41 | // }
42 |
43 | // val job = lifecycleScope.launch {
44 | // println("Coroutine cancel: 1")
45 | // Thread.sleep(500)
46 | // println("Coroutine cancel: 2")
47 | // }
48 |
49 | lifecycleScope.launch {
50 | delay(200)
51 | job.cancel()
52 | }
53 | }
54 |
55 | private suspend fun callbackToSuspend() = suspendCoroutine {
56 | gitHub.contributorsCall("square", "retrofit")
57 | .enqueue(object : Callback> {
58 | override fun onResponse(
59 | call: Call>, response: Response>,
60 | ) {
61 | it.resume(response.body()!!)
62 | }
63 |
64 | override fun onFailure(call: Call>, t: Throwable) {
65 | it.resumeWithException(t)
66 | }
67 | })
68 | }
69 |
70 | private suspend fun callbackToCancellableSuspend() = suspendCancellableCoroutine {
71 | it.invokeOnCancellation {
72 |
73 | }
74 | gitHub.contributorsCall("square", "retrofit")
75 | .enqueue(object : Callback> {
76 | override fun onResponse(
77 | call: Call>, response: Response>,
78 | ) {
79 | it.resume(response.body()!!)
80 | }
81 |
82 | override fun onFailure(call: Call>, t: Throwable) {
83 | it.resumeWithException(t)
84 | }
85 | })
86 | }
87 |
88 | private fun callbackStyle() {
89 | gitHub.contributorsCall("square", "retrofit")
90 | .enqueue(object : Callback> {
91 | override fun onResponse(
92 | call: Call>, response: Response>,
93 | ) {
94 | showContributors(response.body()!!)
95 | }
96 |
97 | override fun onFailure(call: Call>, t: Throwable) {
98 | t.printStackTrace()
99 | }
100 | })
101 | }
102 |
103 | private fun showContributors(contributors: List) = contributors
104 | .map { "${it.login} (${it.contributions})" }
105 | .reduce { acc, s -> "$acc\n$s" }
106 | .let { infoTextView.text = it }
107 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/12_runBlocking.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.lifecycleScope
7 | import com.rengwuxian.coursecoroutines.R
8 | import com.rengwuxian.coursecoroutines.common.gitHub
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.async
11 | import kotlinx.coroutines.coroutineScope
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.runBlocking
14 | import kotlin.concurrent.thread
15 |
16 | fun main() = runBlocking {
17 | val contributors = gitHub.contributors("square", "retrofit")
18 | launch {
19 |
20 | }
21 | }
22 |
23 | class RunBlockingActivity : ComponentActivity() {
24 | private lateinit var infoTextView: TextView
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.layout_1)
29 | infoTextView = findViewById(R.id.infoTextView)
30 |
31 | lifecycleScope.launch(Dispatchers.Main.immediate) {
32 |
33 | }
34 | println()
35 | lifecycleScope.async { }
36 | blockingContributors()
37 | println()
38 | }
39 |
40 | private fun blockingContributors() = runBlocking {
41 | gitHub.contributors("square", "retrofit")
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/1_launch.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.view.View
7 | import androidx.activity.ComponentActivity
8 | import androidx.activity.compose.setContent
9 | import androidx.activity.enableEdgeToEdge
10 | import com.rengwuxian.coursecoroutines.ui.theme.CourseCoroutinesTheme
11 | import kotlinx.coroutines.CoroutineScope
12 | import kotlinx.coroutines.DelicateCoroutinesApi
13 | import kotlinx.coroutines.Dispatchers
14 | import kotlinx.coroutines.ExperimentalCoroutinesApi
15 | import kotlinx.coroutines.launch
16 | import kotlinx.coroutines.newFixedThreadPoolContext
17 | import kotlinx.coroutines.newSingleThreadContext
18 | import java.util.concurrent.Executors
19 | import kotlin.concurrent.thread
20 | import kotlin.coroutines.EmptyCoroutineContext
21 |
22 | class LaunchCoroutineActivity : ComponentActivity() {
23 |
24 | @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | enableEdgeToEdge()
28 | setContent {
29 | CourseCoroutinesTheme {}
30 | }
31 |
32 | thread {
33 |
34 | }
35 |
36 | println("Main thread: ${Thread.currentThread().name}")
37 | val executor = Executors.newCachedThreadPool()
38 | executor.execute {
39 | println("Executor thread: ${Thread.currentThread().name}")
40 | }
41 |
42 | val context = newFixedThreadPoolContext(20, "MyThread")
43 | val context1 = newSingleThreadContext("MyThread")
44 | val scope = CoroutineScope(context)
45 | context.close()
46 | scope.launch {
47 | println("Coroutine thread: ${Thread.currentThread().name}")
48 | }
49 | scope.launch {
50 | println("Coroutine thread: ${Thread.currentThread().name}")
51 | }
52 |
53 | // I/O: input / output 100k
54 |
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/2_suspend.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.fragment.app.Fragment
7 | import androidx.lifecycle.lifecycleScope
8 | import com.rengwuxian.coursecoroutines.R
9 | import com.rengwuxian.coursecoroutines.common.Contributor
10 | import com.rengwuxian.coursecoroutines.common.gitHub
11 | import kotlinx.coroutines.CoroutineScope
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.launch
14 | import retrofit2.Call
15 | import retrofit2.Callback
16 | import retrofit2.Response
17 | import retrofit2.Retrofit
18 |
19 | class SuspendActivity : ComponentActivity() {
20 | private lateinit var infoTextView: TextView
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | setContentView(R.layout.layout_1)
25 | infoTextView = findViewById(R.id.infoTextView)
26 |
27 |
28 | // callbackStyle()
29 | coroutinesStyle()
30 | }
31 |
32 | private fun callbackStyle() {
33 | gitHub.contributorsCall("square", "retrofit")
34 | .enqueue(object : Callback> {
35 | override fun onResponse(
36 | call: Call>, response: Response>,
37 | ) {
38 | showContributors(response.body()!!)
39 | gitHub.contributorsCall("square", "okhttp")
40 | .enqueue(object : Callback> {
41 | override fun onResponse(
42 | call: Call>, response: Response>,
43 | ) {
44 | showContributors(response.body()!!)
45 | }
46 |
47 | override fun onFailure(call: Call>, t: Throwable) {
48 | t.printStackTrace()
49 | }
50 | })
51 | }
52 |
53 | override fun onFailure(call: Call>, t: Throwable) {
54 | t.printStackTrace()
55 | }
56 | })
57 | }
58 |
59 | private fun coroutinesStyle() = CoroutineScope(Dispatchers.Main).launch {
60 | val contributors = gitHub.contributors("square", "retrofit")
61 | showContributors(contributors)
62 | val contributors2 = gitHub.contributors("square", "okhttp")
63 | showContributors(contributors2)
64 | }
65 |
66 | private fun showContributors(contributors: List) = contributors
67 | .map { "${it.login} (${it.contributions})" }
68 | .reduce { acc, s -> "$acc\n$s" }
69 | .let { infoTextView.text = it }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/3_android.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.lifecycleScope
8 | import androidx.lifecycle.viewModelScope
9 | import com.rengwuxian.coursecoroutines.R
10 | import com.rengwuxian.coursecoroutines.common.Contributor
11 | import com.rengwuxian.coursecoroutines.common.gitHub
12 | import kotlinx.coroutines.CoroutineScope
13 | import kotlinx.coroutines.Dispatchers
14 | import kotlinx.coroutines.launch
15 |
16 | class AndroidActivity : ComponentActivity() {
17 | private lateinit var infoTextView: TextView
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.layout_1)
22 | infoTextView = findViewById(R.id.infoTextView)
23 |
24 | // KTX
25 | lifecycleScope.launch {
26 |
27 | }
28 | // ContinuationInterceptor
29 | Dispatchers.Default
30 | // Handler.post()
31 | }
32 |
33 | class MyViewModel : ViewModel() {
34 | fun someFun() {
35 | viewModelScope.launch {
36 |
37 | }
38 | }
39 | }
40 |
41 | private fun coroutinesStyle() = lifecycleScope.launch {
42 | val contributors = gitHub.contributors("square", "retrofit")
43 | showContributors(contributors)
44 | }
45 |
46 | private fun showContributors(contributors: List) = contributors
47 | .map { "${it.login} (${it.contributions})" }
48 | .reduce { acc, s -> "$acc\n$s" }
49 | .let { infoTextView.text = it }
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/4_withContext.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.lifecycleScope
7 | import com.rengwuxian.coursecoroutines.R
8 | import com.rengwuxian.coursecoroutines.common.Contributor
9 | import com.rengwuxian.coursecoroutines.common.gitHub
10 | import kotlinx.coroutines.CoroutineScope
11 | import kotlinx.coroutines.Dispatchers
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.withContext
14 |
15 | class WithContextActivity : ComponentActivity() {
16 | private lateinit var infoTextView: TextView
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContentView(R.layout.layout_1)
21 | infoTextView = findViewById(R.id.infoTextView)
22 |
23 |
24 | CoroutineScope(Dispatchers.Main).launch {
25 | val data = withContext(Dispatchers.IO) {
26 | // 网络代码
27 | "data"
28 | }
29 | val processedData = withContext(Dispatchers.Default) {
30 | // 处理数据
31 | "processed $data"
32 | }
33 | println("Processed data: $processedData")
34 | }
35 | }
36 |
37 | private fun coroutinesStyle() = lifecycleScope.launch {
38 | val contributors = gitHub.contributors("square", "retrofit")
39 | showContributors(contributors)
40 | }
41 |
42 | private fun showContributors(contributors: List) = contributors
43 | .map { "${it.login} (${it.contributions})" }
44 | .reduce { acc, s -> "$acc\n$s" }
45 | .let { infoTextView.text = it }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/5_custom_suspend.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import com.rengwuxian.coursecoroutines.common.Contributor
4 | import com.rengwuxian.coursecoroutines.common.gitHub
5 |
6 | suspend fun getRetrofitContributors(): List {
7 | return gitHub.contributors("square", "retrofit")
8 | }
9 |
10 | suspend fun customSuspendFun() {
11 |
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/6_withContext_2.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.lifecycleScope
7 | import com.rengwuxian.coursecoroutines.R
8 | import com.rengwuxian.coursecoroutines.common.Contributor
9 | import com.rengwuxian.coursecoroutines.common.gitHub
10 | import kotlinx.coroutines.CoroutineScope
11 | import kotlinx.coroutines.Dispatchers
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.withContext
14 |
15 | class WithContext2Activity : ComponentActivity() {
16 | private lateinit var infoTextView: TextView
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContentView(R.layout.layout_1)
21 | infoTextView = findViewById(R.id.infoTextView)
22 |
23 | CoroutineScope(Dispatchers.Main).launch {
24 | val data = getData()
25 | val processedData = processData(data)
26 | println("Processed data: $processedData")
27 | }
28 | }
29 |
30 | private suspend fun getData() = withContext(Dispatchers.IO) {
31 | // 网络代码
32 | "data"
33 | }
34 |
35 | private suspend fun processData(data: String) = withContext(Dispatchers.Default) {
36 | // 处理数据
37 | "processed $data"
38 | }
39 |
40 | private fun coroutinesStyle() = lifecycleScope.launch {
41 | val contributors = contributorsOfRetrofit()
42 | showContributors(contributors)
43 | }
44 |
45 | private suspend fun contributorsOfRetrofit() = gitHub.contributors("square", "retrofit")
46 |
47 | private fun showContributors(contributors: List) = contributors
48 | .map { "${it.login} (${it.contributions})" }
49 | .reduce { acc, s -> "$acc\n$s" }
50 | .let { infoTextView.text = it }
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/7_de_magic.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.HandlerThread
6 | import android.os.Looper
7 | import android.widget.TextView
8 | import androidx.activity.ComponentActivity
9 | import androidx.lifecycle.lifecycleScope
10 | import com.rengwuxian.coursecoroutines.R
11 | import com.rengwuxian.coursecoroutines.common.Contributor
12 | import com.rengwuxian.coursecoroutines.common.gitHub
13 | import kotlinx.coroutines.launch
14 | import kotlin.concurrent.thread
15 |
16 | class DeMagicActivity : ComponentActivity() {
17 | private lateinit var infoTextView: TextView
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.layout_1)
22 | infoTextView = findViewById(R.id.infoTextView)
23 |
24 | lifecycleScope.launch {
25 | // switchToBackground {
26 | // gitHub.contributors("square", "retrofit")
27 | // switchToMain {
28 | // showContributors(contributors)
29 | // }
30 | // }
31 | }
32 | }
33 |
34 | private fun coroutinesStyle() = lifecycleScope.launch {
35 | val contributors = gitHub.contributors("square", "retrofit")
36 | showContributors(contributors)
37 | }
38 |
39 | private fun showContributors(contributors: List) = contributors
40 | .map { "${it.login} (${it.contributions})" }
41 | .reduce { acc, s -> "$acc\n$s" }
42 | .let { infoTextView.text = it }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_1_basics/9_structured_concurrency.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._1_basics
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import androidx.activity.ComponentActivity
6 | import androidx.lifecycle.lifecycleScope
7 | import com.rengwuxian.coursecoroutines.R
8 | import com.rengwuxian.coursecoroutines.common.Contributor
9 | import com.rengwuxian.coursecoroutines.common.gitHub
10 | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
11 | import io.reactivex.rxjava3.disposables.Disposable
12 | import kotlinx.coroutines.CoroutineScope
13 | import kotlinx.coroutines.Job
14 | import kotlinx.coroutines.cancel
15 | import kotlinx.coroutines.delay
16 | import kotlinx.coroutines.launch
17 | import retrofit2.Call
18 | import retrofit2.Callback
19 | import retrofit2.Response
20 |
21 | class StructuredConcurrencyActivity : ComponentActivity() {
22 | private lateinit var infoTextView: TextView
23 | private var diposable: Disposable? = null
24 | private var job: Job? = null
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.layout_1)
29 | infoTextView = findViewById(R.id.infoTextView)
30 |
31 | // Structured concurrency
32 | // 内存泄露(泄漏) memory leak
33 | // GC(垃圾回收器) garbage collector
34 | // static
35 | // JNI
36 | // Android 内存泄露 弱引用
37 | // RxJava
38 |
39 | // diposable = rxStyle()
40 | job = coroutinesStyle()
41 | coroutinesStyle()
42 | }
43 |
44 | override fun onDestroy() {
45 | super.onDestroy()
46 | // diposable?.dispose()
47 | // job?.cancel()
48 | lifecycleScope.cancel()
49 | }
50 |
51 | private fun callbackStyle() {
52 | gitHub.contributorsCall("square", "retrofit")
53 | .enqueue(object : Callback> {
54 | override fun onResponse(
55 | call: Call>, response: Response>,
56 | ) {
57 | showContributors(response.body()!!)
58 | gitHub.contributorsCall("square", "okhttp")
59 | .enqueue(object : Callback> {
60 | override fun onResponse(
61 | call: Call>, response: Response>,
62 | ) {
63 | showContributors(response.body()!!)
64 | }
65 |
66 | override fun onFailure(call: Call>, t: Throwable) {
67 | t.printStackTrace()
68 | }
69 | })
70 | }
71 |
72 | override fun onFailure(call: Call>, t: Throwable) {
73 | t.printStackTrace()
74 | }
75 | })
76 | }
77 |
78 | private fun coroutinesStyle() = lifecycleScope.launch {
79 | launch {
80 |
81 | }
82 | val contributors = gitHub.contributors("square", "retrofit")
83 | val filtered = contributors.filter { it.contributions > 10 }
84 | showContributors(filtered)
85 | }
86 |
87 | private fun rxStyle() = gitHub.contributorsRx("square", "retrofit")
88 | .map { list -> list.filter { it.contributions > 10 } }
89 | .observeOn(AndroidSchedulers.mainThread())
90 | .subscribe(::showContributors)
91 |
92 | private fun showContributors(contributors: List) = contributors
93 | .map { "${it.login} (${it.contributions})" }
94 | .reduce { acc, s -> "$acc\n$s" }
95 | .let { infoTextView.text = it }
96 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/10_structured_exception.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineExceptionHandler
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.launch
7 | import kotlinx.coroutines.runBlocking
8 | import kotlin.coroutines.EmptyCoroutineContext
9 | import kotlin.system.exitProcess
10 |
11 | fun main() = runBlocking {
12 | Thread.setDefaultUncaughtExceptionHandler { t, e ->
13 | println("Caught default: $e")
14 | exitProcess(1)
15 | }
16 | /*val thread = Thread {
17 | try {
18 |
19 | } catch (e: NullPointerException) {
20 |
21 | }
22 | throw RuntimeException("Thread error!")
23 | }*/
24 | /*thread.setUncaughtExceptionHandler { t, e ->
25 | println("Caught $e")
26 | }*/
27 | // thread.start()
28 | val scope = CoroutineScope(EmptyCoroutineContext)
29 | val handler = CoroutineExceptionHandler { _, exception ->
30 | println("Caught in Coroutine: $exception")
31 | }
32 | scope.launch(handler) {
33 | launch {
34 | throw RuntimeException("Error!")
35 | }
36 | launch {
37 |
38 | }
39 | }
40 | delay(10000)
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/11_async_exception.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineExceptionHandler
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Job
6 | import kotlinx.coroutines.async
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.runBlocking
10 | import kotlin.coroutines.EmptyCoroutineContext
11 |
12 | fun main() = runBlocking {
13 | val scope = CoroutineScope(EmptyCoroutineContext)
14 | val handler = CoroutineExceptionHandler { _, exception ->
15 | println("Caught in Coroutine: $exception")
16 | }
17 | scope.async/*(handler)*/ {
18 | val deferred = async {
19 | delay(1000)
20 | throw RuntimeException("Error!")
21 | }
22 | launch(Job()) {
23 | try {
24 | deferred.await()
25 | } catch (e: Exception) {
26 | println("Caught in await: $e")
27 | }
28 | try {
29 | delay(1000)
30 | } catch (e: Exception) {
31 | println("Caught in delay: $e")
32 | }
33 | }
34 | // delay(100)
35 | // cancel()
36 | }
37 | delay(10000)
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/12_SupervisorJob.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineExceptionHandler
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Job
6 | import kotlinx.coroutines.SupervisorJob
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.job
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlinx.coroutines.withContext
12 | import kotlin.coroutines.EmptyCoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(SupervisorJob())
16 | val supervisorJob = SupervisorJob()
17 | val job = Job()
18 | scope.launch {
19 | val handler = CoroutineExceptionHandler { _, exception ->
20 | println("Caught in handler: $exception")
21 | }
22 | launch(SupervisorJob(coroutineContext.job) + handler) {
23 | launch {
24 | throw RuntimeException("Error!")
25 | }
26 | }
27 | }
28 | delay(1000)
29 | println("Parent Job cancelled: ${job.isCancelled}")
30 | delay(10000)
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/1_whats_a_coroutine.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.CoroutineStart
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.async
8 | import kotlinx.coroutines.cancelChildren
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.concurrent.thread
12 | import kotlin.coroutines.ContinuationInterceptor
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | fun main() = runBlocking {
16 | val scope = CoroutineScope(Dispatchers.IO)
17 | var innerJob: Job? = null
18 | var innerScope: CoroutineScope? = null
19 | val outerJob = scope.launch(Dispatchers.Default) {
20 | innerJob = coroutineContext[Job]
21 | innerScope = this
22 | launch {
23 |
24 | }
25 | }
26 | outerJob.cancel()
27 |
28 | scope.async {
29 |
30 | }
31 | println("outerJob: $outerJob")
32 | println("innerJob: $innerJob")
33 | println("outerJob === innerJob: ${outerJob === innerJob}")
34 | println("outerJob === innerScope: ${outerJob === innerScope}")
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/2_parent_children.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.Job
6 | import kotlinx.coroutines.delay
7 | import kotlinx.coroutines.launch
8 | import kotlinx.coroutines.runBlocking
9 | import kotlin.coroutines.EmptyCoroutineContext
10 |
11 | @OptIn(ExperimentalCoroutinesApi::class)
12 | fun main() = runBlocking {
13 | val scope = CoroutineScope(EmptyCoroutineContext)
14 | val initJob = scope.launch {
15 | launch { }
16 | launch { }
17 | }
18 | scope.launch {
19 | initJob.join()
20 | // ???
21 | }
22 | var innerJob: Job? = null
23 | val job = scope.launch {
24 | launch(Job()) {
25 | delay(100)
26 | }
27 | // val customJob = Job()
28 | // innerJob = launch(customJob) {
29 | // delay(100)
30 | // }
31 | }
32 | val startTime = System.currentTimeMillis()
33 | job.join()
34 | val duration = System.currentTimeMillis() - startTime
35 | println("duration: $duration")
36 | // val children = job.children
37 | // println("children count: ${children.count()}")
38 | // println("innerJob === children.first(): ${innerJob === children.first()}")
39 | // println("innerJob.parent === job: ${innerJob?.parent === job}")
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/3_thread_stop.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.runBlocking
4 | import kotlin.concurrent.thread
5 |
6 | fun main() = runBlocking {
7 | val thread = thread {
8 | println("Thread: I'm running!")
9 | Thread.sleep(200)
10 | println("Thread: I'm done!")
11 | }
12 | Thread.sleep(100)
13 | thread.stop()
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/4_thread_interrupt.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.runBlocking
4 | import java.util.concurrent.CountDownLatch
5 | import kotlin.concurrent.thread
6 |
7 | fun main() = runBlocking {
8 | val thread = object : Thread() {
9 | override fun run() {
10 | println("Thread: I'm running!")
11 | try {
12 | Thread.sleep(200)
13 | } catch (e: InterruptedException) {
14 | println("isInterrupted: $isInterrupted")
15 | println("Clearing ...")
16 | return
17 | }
18 | val lock = Object()
19 | try {
20 | lock.wait()
21 | } catch (e: InterruptedException) {
22 | println("isInterrupted: $isInterrupted")
23 | println("Clearing ...")
24 | return
25 | }
26 | val newThread = thread {
27 |
28 | }
29 | newThread.join()
30 | val latch = CountDownLatch(3)
31 | latch.await()
32 | /*var count = 0
33 | while (true) {
34 | if (isInterrupted) {
35 | // clear
36 | return
37 | }
38 | count++
39 | if (count % 100_000_000 == 0) {
40 | println(count)
41 | }
42 | if (count % 1_000_000_000 == 0) {
43 | break
44 | }
45 | }*/
46 | println("Thread: I'm done!")
47 | }
48 | }.apply { start() }
49 | Thread.sleep(100)
50 | thread.interrupt()
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/5_cancel.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CancellationException
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.ensureActive
9 | import kotlinx.coroutines.isActive
10 | import kotlinx.coroutines.job
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlinx.coroutines.suspendCancellableCoroutine
14 | import kotlin.coroutines.EmptyCoroutineContext
15 | import kotlin.coroutines.suspendCoroutine
16 |
17 | fun main() = runBlocking {
18 | val scope = CoroutineScope(EmptyCoroutineContext) // Default
19 | val job = launch(Dispatchers.Default) {
20 | suspendCoroutine {
21 |
22 | }
23 | suspendCancellableCoroutine {
24 |
25 | }
26 | /*var count = 0
27 | while (true) {
28 | // ensureActive()
29 | if (!isActive) {
30 | // clear
31 | throw CancellationException()
32 | }
33 | count++
34 | if (count % 100_000_000 == 0) {
35 | println(count)
36 | }
37 | if (count % 1_000_000_000 == 0) {
38 | break
39 | }
40 | }*/
41 | // InterruptedException
42 | var count = 0
43 | while (true) {
44 | /*if (!isActive) {
45 | // Clear
46 | return@launch
47 | }*/
48 | println("count: ${count++}")
49 | try {
50 | delay(500)
51 | } /*catch (e: CancellationException) {
52 | println("Cancelled")
53 | // Clear
54 | throw e
55 | }*/ finally {
56 | // Clear
57 | }
58 | }
59 | }
60 | delay(1000)
61 | job.cancel()
62 | // Thread.interrupt()
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/6_structural_cancellation.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.cancelAndJoin
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.launch
7 | import kotlinx.coroutines.runBlocking
8 | import kotlin.coroutines.EmptyCoroutineContext
9 | import kotlin.time.measureTime
10 |
11 | fun main() = runBlocking {
12 | val scope = CoroutineScope(EmptyCoroutineContext)
13 | val parentJob = scope.launch {
14 | val childJob = launch {
15 | println("Child job started")
16 | delay(3000)
17 | println("Child job finished")
18 | }
19 | }
20 | delay(1000)
21 | parentJob.cancel()
22 | measureTime { parentJob.join() }.also { println("Duration: $it") }
23 | delay(10000)
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/7_NonCancellable.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CancellationException
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.NonCancellable
8 | import kotlinx.coroutines.cancel
9 | import kotlinx.coroutines.delay
10 | import kotlinx.coroutines.isActive
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlinx.coroutines.withContext
14 | import kotlin.coroutines.EmptyCoroutineContext
15 |
16 | fun main() = runBlocking {
17 | val scope = CoroutineScope(EmptyCoroutineContext)
18 | var childJob: Job? = null
19 | var childJob2: Job? = null
20 | val newParent = Job()
21 | val parentJob = scope.launch {
22 | childJob = launch(NonCancellable) {
23 | println("Child started")
24 | delay(1000)
25 | println("Child stopped")
26 | }
27 | println("childJob parent: ${childJob?.parent}")
28 | childJob2 = launch(newParent) {
29 | println("Child started")
30 | writeInfo()
31 | launch(NonCancellable) {
32 | // Log
33 | }
34 | if (!isActive) {
35 | withContext(NonCancellable) {
36 | // Write to database (Room)
37 | delay(1000)
38 | }
39 | throw CancellationException()
40 | }
41 | try {
42 | delay(3000)
43 | } catch (e: CancellationException) {
44 |
45 | throw e
46 | }
47 | println("Child 2 started")
48 | delay(3000)
49 | println("Child 2 stopped")
50 | }
51 | println("Parent started")
52 | delay(3000)
53 | println("Parent stopped")
54 | }
55 | delay(1500)
56 | newParent.cancel()
57 | delay(10000)
58 | }
59 |
60 | suspend fun writeInfo() = withContext(Dispatchers.IO + NonCancellable) {
61 | // write to file
62 | // read from database (Room)
63 | // write data to file
64 | }
65 |
66 | suspend fun uselessSuspendFun() {
67 | Thread.sleep(1000)
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/8_exception.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CancellationException
4 | import kotlinx.coroutines.CoroutineExceptionHandler
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.SupervisorJob
8 | import kotlinx.coroutines.delay
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.concurrent.thread
12 | import kotlin.coroutines.EmptyCoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | var childJob: Job? = null
17 | val parentJob = scope.launch {
18 | childJob = launch {
19 | launch {
20 | println("Grand child started")
21 | delay(3000)
22 | println("Grand child done")
23 | }
24 | delay(1000)
25 | throw IllegalStateException("User invalid!")
26 | }
27 | println("Parent started")
28 | delay(3000)
29 | println("Parent done")
30 | }
31 | delay(500)
32 | // CoroutineExceptionHandler
33 | // parentJob.cancel()
34 | // println("isActive: parent - ${parentJob.isActive}, child - ${childJob?.isActive}")
35 | // println("isCancelled: parent - ${parentJob.isCancelled}, child - ${childJob?.isCancelled}")
36 | // delay(1000)
37 | // println("isActive: parent - ${parentJob.isActive}, child - ${childJob?.isActive}")
38 | // println("isCancelled: parent - ${parentJob.isCancelled}, child - ${childJob?.isCancelled}")
39 | delay(10000)
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_2_structured_concurrency/9_CoroutineExceptionHandler.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._2_structured_concurrency
2 |
3 | import kotlinx.coroutines.CoroutineExceptionHandler
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.launch
7 | import kotlinx.coroutines.runBlocking
8 | import kotlin.concurrent.thread
9 | import kotlin.coroutines.EmptyCoroutineContext
10 |
11 | fun main() = runBlocking {
12 | val scope = CoroutineScope(EmptyCoroutineContext)
13 | val handler = CoroutineExceptionHandler { _, exception ->
14 | println("Caught $exception")
15 | }
16 | scope.launch(handler) {
17 | launch {
18 | throw RuntimeException("Error!")
19 | }
20 | launch {
21 |
22 | }
23 | }
24 | delay(10000)
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/1_scope_context.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.GlobalScope
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.job
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.ContinuationInterceptor
12 | import kotlin.coroutines.CoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val context = Dispatchers.IO + Job() + Job()
16 | val scope = CoroutineScope(Dispatchers.IO)
17 | val job = scope.launch {
18 | this.coroutineContext[Job]
19 | coroutineContext.job
20 | coroutineContext[ContinuationInterceptor]
21 | }
22 | delay(10000)
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/2_GlobalScope.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.DelicateCoroutinesApi
5 | import kotlinx.coroutines.GlobalScope
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.async
8 | import kotlinx.coroutines.cancel
9 | import kotlinx.coroutines.delay
10 | import kotlinx.coroutines.job
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | @OptIn(DelicateCoroutinesApi::class)
16 | fun main() = runBlocking {
17 | CoroutineScope(EmptyCoroutineContext).launch {
18 |
19 | }
20 | GlobalScope.launch {
21 |
22 | }
23 | GlobalScope.cancel()
24 | val job = GlobalScope.async {
25 | delay(1000)
26 | }
27 | println("job parent: ${job.parent}")
28 | delay(10000)
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/3_coroutineContext.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.GlobalScope
5 | import kotlinx.coroutines.currentCoroutineContext
6 | import kotlinx.coroutines.delay
7 | import kotlinx.coroutines.flow.flow
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.runBlocking
10 | import kotlin.coroutines.ContinuationInterceptor
11 | import kotlin.coroutines.EmptyCoroutineContext
12 | import kotlin.coroutines.coroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | scope.launch {
17 | showDispatcher()
18 | }
19 | delay(10000)
20 | }
21 |
22 | private fun flowFun() {
23 | flow {
24 | coroutineContext
25 | }
26 | GlobalScope.launch {
27 | flow {
28 | currentCoroutineContext()
29 | }
30 | }
31 | }
32 |
33 | private suspend fun showDispatcher() {
34 | delay(1000)
35 | println("Dispatcher: ${coroutineContext[ContinuationInterceptor]}")
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/4_coroutineScope.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Deferred
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.SupervisorJob
8 | import kotlinx.coroutines.async
9 | import kotlinx.coroutines.coroutineScope
10 | import kotlinx.coroutines.delay
11 | import kotlinx.coroutines.job
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.runBlocking
14 | import kotlinx.coroutines.supervisorScope
15 | import kotlin.coroutines.EmptyCoroutineContext
16 |
17 | fun main() = runBlocking {
18 | val scope = CoroutineScope(EmptyCoroutineContext)
19 | scope.launch {
20 | supervisorScope {
21 |
22 | }
23 | val name = try {
24 | coroutineScope {
25 | val deferred1 = async { "rengwuxian" }
26 | val deferred2: Deferred = async { throw RuntimeException("Error!") }
27 | "${deferred1.await()} ${deferred2.await()}"
28 | }
29 | } catch (e: Exception) {
30 | e.message
31 | }
32 | println("Full name: $name")
33 | val startTime1 = System.currentTimeMillis()
34 | coroutineScope {
35 | launch {
36 | delay(2000)
37 | }
38 | delay(1000)
39 | println("Duration within coroutineScope: ${System.currentTimeMillis() - startTime1}")
40 | }
41 | println("Duration of coroutineScope: ${System.currentTimeMillis() - startTime1}")
42 | val startTime = System.currentTimeMillis()
43 | launch {
44 | delay(1000)
45 | println("Duration within launch: ${System.currentTimeMillis() - startTime}")
46 | }
47 | println("Duration of launch: ${System.currentTimeMillis() - startTime}")
48 | }
49 | delay(10000)
50 | }
51 |
52 | private suspend fun someFun() = coroutineScope {
53 | launch {
54 |
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/5_withContext_again.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.coroutineScope
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.launch
7 | import kotlinx.coroutines.runBlocking
8 | import kotlinx.coroutines.withContext
9 | import kotlin.coroutines.EmptyCoroutineContext
10 |
11 | fun main() = runBlocking {
12 | val scope = CoroutineScope(EmptyCoroutineContext)
13 | scope.launch {
14 | withContext(coroutineContext) {
15 |
16 | }
17 | coroutineScope {
18 |
19 | }
20 | }
21 | delay(10000)
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/6_CoroutineName.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineName
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.delay
7 | import kotlinx.coroutines.launch
8 | import kotlinx.coroutines.runBlocking
9 | import kotlinx.coroutines.withContext
10 | import kotlin.concurrent.thread
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | thread { }.name = "MyThread"
15 | val name = CoroutineName("MyCoroutine")
16 | val scope = CoroutineScope(Dispatchers.IO + name)
17 | withContext(name) {
18 |
19 | }
20 | scope.launch {
21 | println("CoroutineName: ${coroutineContext[CoroutineName]?.name}")
22 | }
23 | delay(10000)
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/7_CoroutineContext.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineExceptionHandler
5 | import kotlinx.coroutines.CoroutineName
6 | import kotlinx.coroutines.CoroutineScope
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.Job
9 | import kotlinx.coroutines.delay
10 | import kotlinx.coroutines.launch
11 | import kotlinx.coroutines.runBlocking
12 | import kotlin.coroutines.ContinuationInterceptor
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | @OptIn(ExperimentalStdlibApi::class)
16 | fun main() = runBlocking {
17 | val job1 = Job()
18 | val job2 = Job()
19 | val scope = CoroutineScope(Dispatchers.IO + job1
20 | + CoroutineName("MyCoroutine") + job2)
21 | // [[CoroutineName(MyCoroutine), job2], Dispatchers.IO]
22 | println("job1: $job1, job2: $job2")
23 | println("CoroutineContext: ${scope.coroutineContext}")
24 | scope.launch {
25 | val job: Job? = coroutineContext[Job]
26 | val interceptor: CoroutineDispatcher? = coroutineContext[CoroutineDispatcher]
27 | println("coroutineContext: $coroutineContext")
28 | println("coroutineContext after minusKey() ${coroutineContext.minusKey(Job)}")
29 | }
30 | delay(10000)
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_3_scope_context/8_custom_CoroutineContext.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._3_scope_context
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.launch
6 | import kotlinx.coroutines.runBlocking
7 | import kotlin.coroutines.AbstractCoroutineContextElement
8 | import kotlin.coroutines.CoroutineContext
9 | import kotlin.coroutines.EmptyCoroutineContext
10 | import kotlin.coroutines.coroutineContext
11 |
12 | fun main() = runBlocking {
13 | val scope = CoroutineScope(EmptyCoroutineContext)
14 | val customContext = Logger()
15 | scope.launch(customContext) {
16 | coroutineContext[Logger]?.log()
17 | }
18 | delay(10000)
19 | }
20 |
21 | class Logger : AbstractCoroutineContextElement(Logger) {
22 | companion object Key : CoroutineContext.Key
23 |
24 | suspend fun log() {
25 | println("Current coroutine: $coroutineContext")
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/10_filter.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.filter
7 | import kotlinx.coroutines.flow.filterIsInstance
8 | import kotlinx.coroutines.flow.filterNot
9 | import kotlinx.coroutines.flow.filterNotNull
10 | import kotlinx.coroutines.flow.flow
11 | import kotlinx.coroutines.flow.flowOf
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.runBlocking
14 | import kotlin.coroutines.EmptyCoroutineContext
15 |
16 | fun main() = runBlocking {
17 | val scope = CoroutineScope(EmptyCoroutineContext)
18 | val flow1 = flowOf(1, 2, null, 3, null, 4, 5) // Int?
19 | val flow2 = flowOf(1, 2, 3, "rengwuxian.com", "扔物线",
20 | listOf("A", "B"), listOf(1, 2))
21 | scope.launch {
22 | flow1.filterNotNull().filter { it % 2 == 0 }.collect { println("1: $it") }
23 | flow1.filterNotNull().filterNot { it % 2 == 0 }.collect { println("2: $it") }
24 | flow2.filterIsInstance>().collect { println("3: $it") }
25 | flow2.filterIsInstance(List::class).collect { println("4: $it") }
26 | flow2.filter { it is List<*> && it.firstOrNull()?.let { item -> item is String } == true }
27 | .collect { println("5: $it") }
28 | }
29 | delay(10000)
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/11_distinctUntilChanged.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.distinctUntilChanged
6 | import kotlinx.coroutines.flow.distinctUntilChangedBy
7 | import kotlinx.coroutines.flow.flowOf
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.runBlocking
10 | import kotlin.coroutines.EmptyCoroutineContext
11 |
12 | fun main() = runBlocking {
13 | val scope = CoroutineScope(EmptyCoroutineContext)
14 | val flow1 = flowOf("rengwuxian", "RengWuXian", "rengwuxian.com")
15 | scope.launch {
16 | flow1.distinctUntilChanged().collect { println("1: $it") } // == equals()
17 | flow1.distinctUntilChanged { a, b -> a.uppercase() == b.uppercase() }.collect { println("2: $it") }
18 | flow1.distinctUntilChangedBy { it.uppercase() }.collect { println("3: $it") }
19 | }
20 | delay(10000)
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/12_custom_flow.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.FlowCollector
7 | import kotlinx.coroutines.flow.channelFlow
8 | import kotlinx.coroutines.flow.collect
9 | import kotlinx.coroutines.flow.flow
10 | import kotlinx.coroutines.flow.flowOf
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | fun main() = runBlocking {
16 | val scope = CoroutineScope(EmptyCoroutineContext)
17 | val flow1 = flowOf(1, 2, 3)
18 | scope.launch {
19 | flow1.customOperator().collect { println("1: $it") }
20 | flow1.double().collect { println("2: $it") }
21 | }
22 | delay(10000)
23 | }
24 |
25 | fun Flow.customOperator(): Flow = flow {
26 | collect {
27 | emit(it)
28 | emit(it)
29 | }
30 | }
31 |
32 | fun Flow.double(): Flow = channelFlow {
33 | collect {
34 | send(it * 2)
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/13_timeout_sample_debounce.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.FlowPreview
5 | import kotlinx.coroutines.TimeoutCancellationException
6 | import kotlinx.coroutines.delay
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.collect
9 | import kotlinx.coroutines.flow.debounce
10 | import kotlinx.coroutines.flow.flow
11 | import kotlinx.coroutines.flow.flowOf
12 | import kotlinx.coroutines.flow.sample
13 | import kotlinx.coroutines.flow.timeout
14 | import kotlinx.coroutines.launch
15 | import kotlinx.coroutines.runBlocking
16 | import kotlin.coroutines.EmptyCoroutineContext
17 | import kotlin.time.Duration
18 | import kotlin.time.Duration.Companion.seconds
19 |
20 | @OptIn(FlowPreview::class)
21 | fun main() = runBlocking {
22 | val scope = CoroutineScope(EmptyCoroutineContext)
23 | val flow1 = flow {
24 | emit(0)
25 | delay(500)
26 | emit(1)
27 | delay(800)
28 | emit(2)
29 | delay(900)
30 | emit(3)
31 | delay(1000)
32 | }
33 | scope.launch {
34 | // flow1.timeout(1.seconds).collect { println("1: $it") }
35 | // flow1.sample(1.seconds).collect { println("2: $it") }
36 | flow1.debounce(1.seconds).collect { println("3: $it") }
37 | }
38 | delay(10000)
39 | }
40 |
41 | fun Flow.throttle(timeWindow: Duration): Flow = flow {
42 | var lastTime = 0L
43 | collect {
44 | if (System.currentTimeMillis() - lastTime > timeWindow.inWholeMilliseconds) {
45 | emit(it)
46 | lastTime = System.currentTimeMillis()
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/14_drop_take.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.drop
7 | import kotlinx.coroutines.flow.dropWhile
8 | import kotlinx.coroutines.flow.flowOf
9 | import kotlinx.coroutines.flow.take
10 | import kotlinx.coroutines.flow.takeWhile
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | fun main() = runBlocking {
16 | val scope = CoroutineScope(EmptyCoroutineContext)
17 | val flow1 = flowOf(1, 2, 3, 4, 5)
18 | scope.launch {
19 | flow1.drop(2).collect { println("1: $it") }
20 | flow1.dropWhile { it != 3 }.collect { println("2: $it") }
21 | flow1.take(2).collect { println("3: $it") }
22 | flow1.takeWhile { it != 3 }.collect { println("4: $it") }
23 | }
24 | delay(10000)
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/15_map.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.filter
7 | import kotlinx.coroutines.flow.filterNotNull
8 | import kotlinx.coroutines.flow.flow
9 | import kotlinx.coroutines.flow.flowOf
10 | import kotlinx.coroutines.flow.map
11 | import kotlinx.coroutines.flow.mapLatest
12 | import kotlinx.coroutines.flow.mapNotNull
13 | import kotlinx.coroutines.launch
14 | import kotlinx.coroutines.runBlocking
15 | import kotlin.coroutines.EmptyCoroutineContext
16 |
17 | fun main() = runBlocking {
18 | val scope = CoroutineScope(EmptyCoroutineContext)
19 | val flow1 = flowOf(1, 2, 3, 4, 5)
20 | val flow2 = flow {
21 | delay(100)
22 | emit(1)
23 | delay(100)
24 | emit(2)
25 | delay(100)
26 | emit(3)
27 | }
28 | scope.launch {
29 | flow1.map { if (it == 3) null else it + 1 }.collect { println("1: $it") }
30 | flow1.mapNotNull { if (it == 3) null else it + 1 }.collect { println("2: $it") }
31 | flow1.map { if (it == 3) null else it + 1 }.filterNotNull()
32 | flow1.filter { it != 3 }.map { it + 1 }
33 | flow2.mapLatest { delay(120); it + 1}.collect { println("3: $it") }
34 | }
35 | delay(10000)
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/16_transform.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.flow
7 | import kotlinx.coroutines.flow.flowOf
8 | import kotlinx.coroutines.flow.map
9 | import kotlinx.coroutines.flow.mapNotNull
10 | import kotlinx.coroutines.flow.transform
11 | import kotlinx.coroutines.flow.transformLatest
12 | import kotlinx.coroutines.flow.transformWhile
13 | import kotlinx.coroutines.launch
14 | import kotlinx.coroutines.runBlocking
15 | import kotlin.coroutines.EmptyCoroutineContext
16 |
17 | fun main() = runBlocking {
18 | val scope = CoroutineScope(EmptyCoroutineContext)
19 | val flow1 = flowOf(1, 2, 3, 4, 5)
20 | val flow2 = flow {
21 | delay(100)
22 | emit(1)
23 | delay(100)
24 | emit(2)
25 | delay(100)
26 | emit(3)
27 | }
28 | scope.launch {
29 | flow1.transform {
30 | if (it > 0) {
31 | repeat(it) { _ ->
32 | emit("$it - hahaha")
33 | }
34 | }
35 | }.collect { println("1: $it") }
36 | flow1.transformWhile {
37 | if (it > 3) return@transformWhile false
38 | if (it > 0) {
39 | repeat(it) { _ ->
40 | emit("$it - hahaha")
41 | }
42 | }
43 | true
44 | }.collect { println("2: $it") }
45 | flow2.transformLatest {
46 | delay(50)
47 | emit("$it - start")
48 | delay(100)
49 | emit("$it - end")
50 | }.collect { println("3: $it") }
51 | }
52 | delay(10000)
53 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/17_withIndex.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.collectIndexed
7 | import kotlinx.coroutines.flow.flowOf
8 | import kotlinx.coroutines.flow.withIndex
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | val scope = CoroutineScope(EmptyCoroutineContext)
15 | val flow1 = flowOf(1, 2, 3, 4, 5)
16 | scope.launch {
17 | flow1.withIndex().collect { (index, data) ->
18 | println("1: $index - $data")
19 | }
20 | flow1.collectIndexed { index, value ->
21 | println("1: $index - $value")
22 | }
23 | }
24 | delay(10000)
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/18_reduce_fold.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.flowOf
7 | import kotlinx.coroutines.flow.fold
8 | import kotlinx.coroutines.flow.reduce
9 | import kotlinx.coroutines.flow.runningFold
10 | import kotlinx.coroutines.flow.runningReduce
11 | import kotlinx.coroutines.flow.scan
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.runBlocking
14 | import kotlin.coroutines.EmptyCoroutineContext
15 |
16 | fun main() = runBlocking {
17 | val scope = CoroutineScope(EmptyCoroutineContext)
18 | val flow1 = flowOf(1, 2, 3, 4, 5)
19 | val list = listOf(1, 2, 3, 4, 5)
20 | list.reduce { acc, i -> acc + i }.let { println("List reduced to $it") }
21 | list.runningReduce { acc, i -> acc + i }.let { println("New List: $it") }
22 | list.fold(10) { acc, i -> acc + i }.let { println("List folded to $it") }
23 | list.fold("ha") { acc, i -> "$acc - $i" }.let { println("List folded to string: $it") }
24 | list.runningFold("ha") { acc, i -> "$acc - $i" }.let { println("New String List: $it") }
25 | scope.launch {
26 | val sum = flow1.reduce { accumulator, value -> accumulator + value }
27 | println("Sum is $sum")
28 | flow1.runningReduce { accumulator, value -> accumulator + value }
29 | .collect { println("runningReduce: $it") }
30 | flow1.fold("ha") { acc, i -> "$acc - $i" }.let { println("Flow folded to string: $it") }
31 | flow1.runningFold("ha") { acc, i -> "$acc - $i" }
32 | .collect { println("Flow.runningFold(): $it") }
33 | flow1.scan("ha") { acc, i -> "$acc - $i" }
34 | .collect { println("Flow.scan(): $it") }
35 | }
36 | delay(10000)
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/19_onEach.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.collect
6 | import kotlinx.coroutines.flow.filter
7 | import kotlinx.coroutines.flow.flowOf
8 | import kotlinx.coroutines.flow.onEach
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | val scope = CoroutineScope(EmptyCoroutineContext)
15 | val flow1 = flowOf(1, 2, 3, 4, 5)
16 | scope.launch {
17 | flow1.onEach {
18 | println("onEach 1: $it")
19 | }.onEach {
20 | println("onEach 2: $it")
21 | }.filter {
22 | it % 2 == 0
23 | }.onEach {
24 | println("onEach 3: $it")
25 | }.collect {
26 | println("collect: $it")
27 | }
28 | }
29 | delay(10000)
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/1_overview.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.runBlocking
4 |
5 | fun main() = runBlocking {
6 | // StateFlow
7 | // SharedFlow // event flow
8 | // Flow // data flow
9 | // Channel
10 | // async { }
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/20_chunked.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.chunked
7 | import kotlinx.coroutines.flow.collect
8 | import kotlinx.coroutines.flow.flowOf
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | @OptIn(ExperimentalCoroutinesApi::class)
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val flow1 = flowOf(1, 2, 3, 4, 5)
17 | scope.launch {
18 | flow1.chunked(2).collect { println("chunked: $it") }
19 | }
20 | delay(10000)
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/21_try_catch.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import com.rengwuxian.coursecoroutines.common.unstableGitHub
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.flow
7 | import kotlinx.coroutines.flow.map
8 | import kotlinx.coroutines.flow.onEach
9 | import kotlinx.coroutines.flow.transform
10 | import kotlinx.coroutines.launch
11 | import kotlinx.coroutines.runBlocking
12 | import java.util.concurrent.TimeoutException
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | fun main() = runBlocking {
16 | val scope = CoroutineScope(EmptyCoroutineContext)
17 | val flow1 = flow {
18 | try {
19 | for (i in 1..5) {
20 | // 数据库读数据
21 | // 网络请求
22 | emit(i)
23 | }
24 | } catch (e: Exception) {
25 | println("Error in flow(): $e")
26 | throw e
27 | }
28 | }.map { throw NullPointerException() }
29 | .onEach { throw NullPointerException() }
30 | .transform {
31 | val data = it * 2
32 | emit(data)
33 | emit(data)
34 | }
35 | // Exception Transparency
36 | scope.launch {
37 | try {
38 | flow1.collect {
39 | val contributors = unstableGitHub.contributors("square", "retrofit")
40 | println("Contributors: $contributors")
41 | }
42 | } catch (e: TimeoutException) {
43 | println("Network error: $e")
44 | } catch (e: NullPointerException) {
45 | println("Null data: $e")
46 | }
47 | }
48 | delay(10000)
49 | }
50 |
51 | private fun fun1() {
52 | fun2()
53 | }
54 |
55 | private fun fun2() {
56 | fun3()
57 | }
58 |
59 | private fun fun3() {
60 | throw NullPointerException("User null")
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/22_catch.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import com.rengwuxian.coursecoroutines.common.unstableGitHub
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.catch
7 | import kotlinx.coroutines.flow.flow
8 | import kotlinx.coroutines.flow.onEach
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import java.util.concurrent.TimeoutException
12 | import kotlin.coroutines.EmptyCoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val flow1 = flow {
17 | for (i in 1..5) {
18 | // 数据库读数据
19 | // 网络请求
20 | if (i == 3) {
21 | throw RuntimeException("flow() error")
22 | } else {
23 | emit(i)
24 | }
25 | }
26 | }.catch {
27 | println("catch(): $it")
28 | emit(100)
29 | emit(200)
30 | emit(300)
31 | // throw RuntimeException("Exception from catch()")
32 | }/*.onEach { throw RuntimeException("Exception from onEach()") }
33 | .catch { println("catch() 2: $it") }*/
34 | scope.launch {
35 | try {
36 | flow1.collect {
37 | /*val contributors = unstableGitHub.contributors("square", "retrofit")
38 | println("Contributors: $contributors")*/
39 | println("Data: $it")
40 | }
41 | } catch (e: TimeoutException) {
42 | println("Network error: $e")
43 | }
44 | }
45 | delay(10000)
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/23_retry.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.flow
6 | import kotlinx.coroutines.flow.map
7 | import kotlinx.coroutines.flow.retry
8 | import kotlinx.coroutines.flow.retryWhen
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import java.util.concurrent.TimeoutException
12 | import kotlin.coroutines.EmptyCoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val flow1 = flow {
17 | for (i in 1..5) {
18 | // 数据库读数据
19 | // 网络请求
20 | if (i == 3) {
21 | throw RuntimeException("flow() error")
22 | } else {
23 | emit(i)
24 | }
25 | }
26 | }.map { it * 2 }.retry(3) {
27 | it is RuntimeException
28 | }/*.retryWhen { cause, attempt -> }*/
29 | scope.launch {
30 | try {
31 | flow1.collect {
32 | println("Data: $it")
33 | }
34 | } catch (e: TimeoutException) {
35 | println("Network error: $e")
36 | } catch (e: RuntimeException) {
37 | println("RuntimeException: $e")
38 | }
39 | }
40 | delay(10000)
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/24_onStart_onCompletion_onEmpty.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.catch
6 | import kotlinx.coroutines.flow.flow
7 | import kotlinx.coroutines.flow.onCompletion
8 | import kotlinx.coroutines.flow.onEmpty
9 | import kotlinx.coroutines.flow.onStart
10 | import kotlinx.coroutines.launch
11 | import kotlinx.coroutines.runBlocking
12 | import kotlin.coroutines.EmptyCoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val flow1 = flow {
17 | try {
18 | for (i in 1..5) {
19 | emit(i)
20 | }
21 | } catch (e: Exception) {
22 | println("try / catch: $e")
23 | }
24 | }.onStart {
25 | println("onStart 1")
26 | throw RuntimeException("onStart error")
27 | }
28 | .onStart { println("onStart 2") }
29 | .onCompletion {
30 | println("onCompletion: $it")
31 | }.onEmpty { println("onEmpty") }
32 | .catch { println("catch: $it") }
33 | scope.launch {
34 | flow1.collect {
35 | println("Data: $it")
36 | }
37 | }
38 | delay(10000)
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/25_flowOn.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineName
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.currentCoroutineContext
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.flow.channelFlow
9 | import kotlinx.coroutines.flow.collect
10 | import kotlinx.coroutines.flow.flow
11 | import kotlinx.coroutines.flow.flowOn
12 | import kotlinx.coroutines.flow.launchIn
13 | import kotlinx.coroutines.flow.map
14 | import kotlinx.coroutines.flow.onEach
15 | import kotlinx.coroutines.launch
16 | import kotlinx.coroutines.newFixedThreadPoolContext
17 | import kotlinx.coroutines.plus
18 | import kotlinx.coroutines.runBlocking
19 | import kotlin.coroutines.EmptyCoroutineContext
20 |
21 | fun main() = runBlocking {
22 | val scope = CoroutineScope(EmptyCoroutineContext)
23 | val flow1 = flow {
24 | println("CoroutineContext in flow(): ${currentCoroutineContext()}")
25 | for (i in 1..5) {
26 | emit(i)
27 | }
28 | }.map {
29 | println("CoroutineContext in map() 1: ${currentCoroutineContext()}")
30 | it * 2
31 | }.flowOn(Dispatchers.IO).flowOn(Dispatchers.Default)
32 | .map {
33 | println("CoroutineContext in map() 2: ${currentCoroutineContext()}")
34 | it * 2
35 | }.flowOn(newFixedThreadPoolContext(2, "TestPool"))
36 | val flow2 = channelFlow {
37 | println("CoroutineContext in channelFlow(): ${currentCoroutineContext()}")
38 | for (i in 1..5) {
39 | send(i)
40 | }
41 | }.map { it }.flowOn(Dispatchers.IO)
42 | scope.launch {
43 | /*flow1.map {
44 | it + 1
45 | }.onEach {
46 | println("Data: $it - ${currentCoroutineContext()}")
47 | }.flowOn(Dispatchers.IO)
48 | .collect {}*/
49 | flow2.collect()
50 | }
51 | /*flow1.map {
52 | it + 1
53 | }.onEach {
54 | println("Data: $it - ${currentCoroutineContext()}")
55 | }.launchIn(scope + Dispatchers.IO)*/
56 | delay(10000)
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/26_buffer.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.channels.BufferOverflow
6 | import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.flow.buffer
9 | import kotlinx.coroutines.flow.collectLatest
10 | import kotlinx.coroutines.flow.conflate
11 | import kotlinx.coroutines.flow.flow
12 | import kotlinx.coroutines.flow.flowOn
13 | import kotlinx.coroutines.flow.map
14 | import kotlinx.coroutines.flow.mapLatest
15 | import kotlinx.coroutines.launch
16 | import kotlinx.coroutines.runBlocking
17 | import kotlinx.coroutines.withContext
18 | import kotlin.concurrent.thread
19 | import kotlin.coroutines.EmptyCoroutineContext
20 |
21 | fun main() = runBlocking {
22 | val scope = CoroutineScope(EmptyCoroutineContext)
23 | val start = System.currentTimeMillis()
24 | val flow1 = flow {
25 | for (i in 1..5) {
26 | emit(i)
27 | println("Emitted: $i - ${System.currentTimeMillis() - start}ms")
28 | }
29 | }.buffer(1)
30 | .flowOn(Dispatchers.IO)
31 | .buffer(2)
32 | // .conflate()
33 | .map { it + 1 }
34 | .map { it * 2 }
35 | scope.launch {
36 | flow1.mapLatest { it }.buffer(0).collect {
37 | delay(1000)
38 | println("Data: $it")
39 | }
40 | }
41 | delay(10000)
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/27_merge.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.asFlow
7 | import kotlinx.coroutines.flow.collect
8 | import kotlinx.coroutines.flow.combine
9 | import kotlinx.coroutines.flow.combineTransform
10 | import kotlinx.coroutines.flow.flatMapConcat
11 | import kotlinx.coroutines.flow.flatMapLatest
12 | import kotlinx.coroutines.flow.flatMapMerge
13 | import kotlinx.coroutines.flow.flattenConcat
14 | import kotlinx.coroutines.flow.flattenMerge
15 | import kotlinx.coroutines.flow.flow
16 | import kotlinx.coroutines.flow.flowOf
17 | import kotlinx.coroutines.flow.map
18 | import kotlinx.coroutines.flow.merge
19 | import kotlinx.coroutines.flow.zip
20 | import kotlinx.coroutines.launch
21 | import kotlinx.coroutines.runBlocking
22 | import kotlin.coroutines.EmptyCoroutineContext
23 |
24 | @OptIn(ExperimentalCoroutinesApi::class)
25 | fun main() = runBlocking {
26 | val scope = CoroutineScope(EmptyCoroutineContext)
27 | val flow1 = flow {
28 | delay(500)
29 | emit(1)
30 | delay(500)
31 | emit(2)
32 | delay(500)
33 | emit(3)
34 | }
35 | val flow2 = flow {
36 | delay(250)
37 | emit(4)
38 | delay(500)
39 | emit(5)
40 | delay(500)
41 | emit(6)
42 | }
43 | val mergedFlow = merge(flow1, flow2)
44 | val flowList = listOf(flow1, flow2)
45 | val mergedFlowFromList = flowList.merge()
46 | val flowFlow = flowOf(flow1, flow2) // flatten
47 | val concattedFlowFlow = flowFlow.flattenConcat() // concatenate
48 | val mergedFlowFlow = flowFlow.flattenMerge()
49 | val concattedMappedFlow = flow1.flatMapConcat { from -> (1..from).asFlow().map { "$from - $it" } }
50 | val mergedMappedFlow = flow1.flatMapMerge { from -> (1..from).asFlow().map { "$from - $it" } }
51 | val latestMappedFlow = flow1.flatMapLatest { from -> (1..from).asFlow().map { "$from - $it" } }
52 | val combinedFlow = flow1.combine(flow2) { a, b -> "$a - $b" }
53 | val combinedFlow2 = combine(flow1, flow2, flow1) { a, b, c -> "$a - $b - $c" }
54 | flow1.combineTransform(flow2) { a, b -> emit("$a - $b") }
55 | val zippedFlow = flow1.zip(flow2) { a, b -> "$a - $b" }
56 | scope.launch {
57 | zippedFlow.collect { println(it) }
58 | }
59 | delay(10000)
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/28_convert.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.count
6 | import kotlinx.coroutines.flow.first
7 | import kotlinx.coroutines.flow.firstOrNull
8 | import kotlinx.coroutines.flow.flowOf
9 | import kotlinx.coroutines.flow.last
10 | import kotlinx.coroutines.flow.lastOrNull
11 | import kotlinx.coroutines.flow.produceIn
12 | import kotlinx.coroutines.flow.single
13 | import kotlinx.coroutines.flow.singleOrNull
14 | import kotlinx.coroutines.flow.toCollection
15 | import kotlinx.coroutines.flow.toList
16 | import kotlinx.coroutines.flow.toSet
17 | import kotlinx.coroutines.launch
18 | import kotlinx.coroutines.runBlocking
19 | import kotlin.coroutines.EmptyCoroutineContext
20 |
21 | fun main() = runBlocking {
22 | val scope = CoroutineScope(EmptyCoroutineContext)
23 | val flow1 = flowOf(1, 2, 3, 4, 5)
24 | val channel = flow1.produceIn(scope) // Channel produce()
25 | scope.launch {
26 | println("First: ${flow1.first()}")
27 | println("First with condition: ${flow1.first { it > 2 }}")
28 | try {
29 | flowOf().first()
30 | } catch (e: NoSuchElementException) {
31 | println("No element")
32 | }
33 | println("firstOrNull(): ${flow1.firstOrNull { it > 5 }}")
34 | // terminal operator
35 | flow1.last()
36 | flow1.lastOrNull()
37 | try {
38 | flow1.single()
39 | } catch (e: Exception) {
40 |
41 | }
42 | flow1.singleOrNull()
43 | println("count(): ${flow1.count { it > 2 }}")
44 | val list = mutableListOf()
45 | println("List: ${flow1.toList(list)}")
46 | flow1.toSet() // Set
47 | flow1.toCollection(list)
48 | }
49 | delay(10000)
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/29_SharedFlow.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.GlobalScope
5 | import kotlinx.coroutines.channels.awaitClose
6 | import kotlinx.coroutines.delay
7 | import kotlinx.coroutines.flow.SharingStarted
8 | import kotlinx.coroutines.flow.callbackFlow
9 | import kotlinx.coroutines.flow.collect
10 | import kotlinx.coroutines.flow.count
11 | import kotlinx.coroutines.flow.flow
12 | import kotlinx.coroutines.flow.launchIn
13 | import kotlinx.coroutines.flow.shareIn
14 | import kotlinx.coroutines.launch
15 | import kotlinx.coroutines.runBlocking
16 | import kotlin.coroutines.EmptyCoroutineContext
17 |
18 | fun main() = runBlocking {
19 | Ticker.start()
20 | val scope = CoroutineScope(EmptyCoroutineContext)
21 | val flow1 = flow {
22 | emit(1)
23 | delay(1000)
24 | emit(2)
25 | delay(1000)
26 | emit(3)
27 | }
28 | val flow2 = callbackFlow {
29 | Ticker.subscribe { trySend(it) }
30 | awaitClose()
31 | }
32 | scope.launch {
33 | delay(2500)
34 | flow2.collect {
35 | println("flow2 - 1: $it")
36 | }
37 | }
38 | scope.launch {
39 | delay(1500)
40 | flow2.collect {
41 | println("flow2 - 2: $it")
42 | }
43 | }
44 | val sharedFlow = flow1.shareIn(scope, SharingStarted.Eagerly)
45 | /*scope.launch {
46 | delay(500)
47 | sharedFlow.collect {
48 | println("SharedFlow in Coroutine 1: $it")
49 | }
50 | }
51 | // Channel: hot
52 | // FLow: cold
53 | scope.launch {
54 | delay(1500)
55 | sharedFlow.collect {
56 | println("SharedFlow in Coroutine 2: $it")
57 | }
58 | }*/
59 | delay(10000)
60 | }
61 |
62 | object Ticker {
63 | private var time = 0
64 | set(value) { // Kotlin setter
65 | field = value
66 | subscribers.forEach { it(value) }
67 | }
68 |
69 | private val subscribers = mutableListOf<(Int) -> Unit>()
70 |
71 | fun subscribe(subscriber: (Int) -> Unit) {
72 | subscribers += subscriber
73 | }
74 |
75 | fun start() {
76 | GlobalScope.launch {
77 | while (true) {
78 | delay(1000)
79 | time++
80 | }
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/2_produce.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import com.rengwuxian.coursecoroutines.common.gitHub
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.ExperimentalCoroutinesApi
6 | import kotlinx.coroutines.channels.produce
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.isActive
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | @OptIn(ExperimentalCoroutinesApi::class)
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val receiver = scope.produce {
17 | while (isActive) {
18 | val data = gitHub.contributors("square", "retrofit")
19 | send(data)
20 | }
21 | }
22 | launch {
23 | delay(5000)
24 | while (isActive) {
25 | println("Contributors: ${receiver.receive()}")
26 | }
27 | }
28 | delay(10000)
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/30_shareIn.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.cancel
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.SharingStarted
7 | import kotlinx.coroutines.flow.flow
8 | import kotlinx.coroutines.flow.shareIn
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | val scope = CoroutineScope(EmptyCoroutineContext)
15 | val flow1 = flow {
16 | emit(1)
17 | delay(1000)
18 | emit(2)
19 | delay(1000)
20 | emit(3)
21 | }
22 | val sharedFlow = flow1.shareIn(scope, SharingStarted.WhileSubscribed(), 2)
23 | scope.launch {
24 | val parent = this
25 | launch {
26 | delay(4000)
27 | parent.cancel()
28 | }
29 | delay(1500)
30 | sharedFlow.collect {
31 | println("SharedFlow in Coroutine 1: $it")
32 | }
33 | }
34 | scope.launch {
35 | delay(5000)
36 | sharedFlow.collect {
37 | println("SharedFlow in Coroutine 2: $it")
38 | }
39 | }
40 | delay(10000)
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/31_MutableSharedFlow.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.cancel
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.MutableSharedFlow
7 | import kotlinx.coroutines.flow.SharingStarted
8 | import kotlinx.coroutines.flow.asSharedFlow
9 | import kotlinx.coroutines.flow.flow
10 | import kotlinx.coroutines.flow.shareIn
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | fun main() = runBlocking {
16 | val scope = CoroutineScope(EmptyCoroutineContext)
17 | val flow1 = flow {
18 | emit(1)
19 | delay(1000)
20 | emit(2)
21 | delay(1000)
22 | emit(3)
23 | }
24 | val clickFlow = MutableSharedFlow()
25 | val readonlyClickFlow = clickFlow.asSharedFlow()
26 | val sharedFlow = flow1.shareIn(scope, SharingStarted.WhileSubscribed(), 2)
27 | scope.launch {
28 | clickFlow.emit("Hello")
29 | delay(1000)
30 | clickFlow.emit("Hi")
31 | delay(1000)
32 | clickFlow.emit("你好")
33 | val parent = this
34 | launch {
35 | delay(4000)
36 | parent.cancel()
37 | }
38 | delay(1500)
39 | sharedFlow.collect {
40 | println("SharedFlow in Coroutine 1: $it")
41 | }
42 | }
43 | scope.launch {
44 | delay(5000)
45 | sharedFlow.collect {
46 | println("SharedFlow in Coroutine 2: $it")
47 | }
48 | }
49 | delay(10000)
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/32_StateFlow.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.flow.MutableStateFlow
6 | import kotlinx.coroutines.flow.asStateFlow
7 | import kotlinx.coroutines.flow.collect
8 | import kotlinx.coroutines.flow.flow
9 | import kotlinx.coroutines.flow.stateIn
10 | import kotlinx.coroutines.launch
11 | import kotlinx.coroutines.runBlocking
12 | import kotlin.coroutines.EmptyCoroutineContext
13 |
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val name = MutableStateFlow("rengwuxian")
17 | val flow1 = flow {
18 | emit(1)
19 | delay(1000)
20 | emit(2)
21 | delay(1000)
22 | emit(3)
23 | }
24 | name.asStateFlow()
25 | val state = flow1.stateIn(scope)
26 | scope.launch {
27 | name.collect {
28 | println("State: $it")
29 | }
30 | }
31 | scope.launch {
32 | delay(2000)
33 | name.emit("扔物线")
34 | }
35 | delay(10000)
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/3_channel.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import com.rengwuxian.coursecoroutines.common.Contributor
4 | import com.rengwuxian.coursecoroutines.common.gitHub
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.ExperimentalCoroutinesApi
7 | import kotlinx.coroutines.channels.Channel
8 | import kotlinx.coroutines.channels.produce
9 | import kotlinx.coroutines.delay
10 | import kotlinx.coroutines.isActive
11 | import kotlinx.coroutines.launch
12 | import kotlinx.coroutines.runBlocking
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | @OptIn(ExperimentalCoroutinesApi::class)
16 | fun main() = runBlocking {
17 | val scope = CoroutineScope(EmptyCoroutineContext)
18 | val channel = Channel>()
19 | scope.launch {
20 | channel.send(gitHub.contributors("square", "retrofit"))
21 | }
22 | scope.launch {
23 | while (isActive) {
24 | channel.receive()
25 | }
26 | }
27 | scope.launch {
28 | while (isActive) {
29 | channel.receive()
30 | }
31 | }
32 | val receiver = scope.produce {
33 | while (isActive) {
34 | val data = gitHub.contributors("square", "retrofit")
35 | send(data)
36 | }
37 | }
38 | launch {
39 | delay(5000)
40 | while (isActive) {
41 | println("Contributors: ${receiver.receive()}")
42 | }
43 | }
44 | delay(10000)
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/4_channel_api.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import com.rengwuxian.coursecoroutines.common.Contributor
4 | import com.rengwuxian.coursecoroutines.common.gitHub
5 | import kotlinx.coroutines.CancellationException
6 | import kotlinx.coroutines.CoroutineScope
7 | import kotlinx.coroutines.channels.BufferOverflow
8 | import kotlinx.coroutines.channels.Channel
9 | import kotlinx.coroutines.channels.Channel.Factory.BUFFERED
10 | import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
11 | import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
12 | import kotlinx.coroutines.delay
13 | import kotlinx.coroutines.isActive
14 | import kotlinx.coroutines.launch
15 | import kotlinx.coroutines.runBlocking
16 | import java.io.FileWriter
17 | import kotlin.coroutines.EmptyCoroutineContext
18 |
19 | fun main() = runBlocking {
20 | val scope = CoroutineScope(EmptyCoroutineContext)
21 | val fileChannel = Channel() { it.close() }
22 | fileChannel.send(FileWriter("test.txt"))
23 | // val channel = Channel>(8, BufferOverflow.DROP_OLDEST)
24 | // val channel = Channel>(1, BufferOverflow.DROP_OLDEST)
25 | val channel = Channel>(CONFLATED)
26 | scope.launch {
27 | channel.send(gitHub.contributors("square", "retrofit"))
28 | channel.close()
29 | channel.close(IllegalStateException("Data error!"))
30 | channel.receive()
31 | channel.receive()
32 | channel.send(gitHub.contributors("square", "retrofit"))
33 | channel.trySend(gitHub.contributors("square", "retrofit"))
34 | channel.tryReceive()
35 | }
36 | launch {
37 | for (data in channel) {
38 | println("Contributors: $data")
39 | }
40 | /*while (isActive) {
41 | val contributors = channel.receive()
42 | println("Contributors: $contributors")
43 | }*/
44 | }
45 | delay(1000)
46 | channel.cancel()
47 | delay(10000)
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/5_actor.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.ObsoleteCoroutinesApi
5 | import kotlinx.coroutines.channels.Channel
6 | import kotlinx.coroutines.channels.actor
7 | import kotlinx.coroutines.channels.produce
8 | import kotlinx.coroutines.delay
9 | import kotlinx.coroutines.launch
10 | import kotlinx.coroutines.runBlocking
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | @OptIn(ObsoleteCoroutinesApi::class)
14 | fun main() = runBlocking {
15 | val scope = CoroutineScope(EmptyCoroutineContext)
16 | val sender = scope.actor {
17 | for (num in this) {
18 | println("Number: $num")
19 | }
20 | }
21 | scope.launch {
22 | for (num in 1..100) {
23 | sender.send(num)
24 | delay(1000)
25 | }
26 | }
27 | delay(10000)
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/6_flow.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.flow
7 | import kotlinx.coroutines.flow.map
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.runBlocking
10 | import kotlinx.coroutines.withContext
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | val list = buildList {
15 | // while (true) {
16 | add(getData())
17 | // }
18 | }
19 | for (num in list) {
20 | println("List item: $num")
21 | }
22 | val nums = sequence {
23 | while (true) {
24 | yield(1)
25 | }
26 | }.map { "number $it" }
27 | for (num in nums) {
28 | println(num)
29 | }
30 |
31 | val numsFlow = flow {
32 | while (true) {
33 | emit(getData())
34 | }
35 | }.map { "number $it" }
36 | val scope = CoroutineScope(EmptyCoroutineContext)
37 | scope.launch {
38 | numsFlow.collect {
39 | println(it)
40 | }
41 | }
42 | delay(10000)
43 | }
44 |
45 | suspend fun getData(): Int {
46 | return 1
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/7_flow_use_case.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.flow
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.runBlocking
10 | import kotlinx.coroutines.withContext
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | val numsFlow = flow {
15 | emit(1)
16 | delay(100)
17 | emit(2)
18 | }
19 | val scope = CoroutineScope(EmptyCoroutineContext)
20 | scope.launch {
21 | // showWeather(weatherFlow)
22 | weatherFlow.collect {
23 | println("Weather: $it")
24 | }
25 | // log("done")
26 | /*numsFlow.collect {
27 | println("A: $it")
28 | }*/
29 | }
30 | scope.launch {
31 | delay(50)
32 | numsFlow.collect {
33 | println("B: $it")
34 | }
35 | }
36 | delay(10000)
37 | }
38 |
39 | val weatherFlow = flow {
40 | while (true) {
41 | emit(getWeather())
42 | delay(60000)
43 | }
44 | }
45 |
46 | suspend fun showWeather(flow: Flow) {
47 | flow.collect {
48 | println("Weather: $it")
49 | }
50 | }
51 |
52 | suspend fun getWeather() = withContext(Dispatchers.IO) {
53 | "Sunny"
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/8_build.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import com.rengwuxian.coursecoroutines.common.Contributor
4 | import com.rengwuxian.coursecoroutines.common.gitHub
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.cancel
7 | import kotlinx.coroutines.channels.Channel
8 | import kotlinx.coroutines.channels.awaitClose
9 | import kotlinx.coroutines.delay
10 | import kotlinx.coroutines.flow.asFlow
11 | import kotlinx.coroutines.flow.callbackFlow
12 | import kotlinx.coroutines.flow.channelFlow
13 | import kotlinx.coroutines.flow.consumeAsFlow
14 | import kotlinx.coroutines.flow.flow
15 | import kotlinx.coroutines.flow.flowOf
16 | import kotlinx.coroutines.flow.receiveAsFlow
17 | import kotlinx.coroutines.launch
18 | import kotlinx.coroutines.runBlocking
19 | import kotlinx.coroutines.suspendCancellableCoroutine
20 | import retrofit2.Call
21 | import retrofit2.Callback
22 | import retrofit2.Response
23 | import kotlin.coroutines.EmptyCoroutineContext
24 | import kotlin.coroutines.cancellation.CancellationException
25 |
26 | fun main() = runBlocking {
27 | val flow1 = flowOf(1, 2, 3)
28 | val flow2 = listOf(1, 2, 3).asFlow()
29 | val flow3 = setOf(1, 2, 3).asFlow()
30 | val flow4 = sequenceOf(1, 2, 3).asFlow()
31 | val channel = Channel()
32 | val flow5 = channel.consumeAsFlow()
33 | val flow6 = channel.receiveAsFlow()
34 | val flow7 = channelFlow {
35 | launch {
36 | delay(2000)
37 | send(2)
38 | }
39 | delay(1000)
40 | send(1)
41 | }
42 | val flow8 = flow {
43 | launch {
44 | delay(2000)
45 | emit(2)
46 | }
47 | delay(1000)
48 | emit(1)
49 | }
50 | val flow9 = callbackFlow {
51 | gitHub.contributorsCall("square", "retrofit")
52 | .enqueue(object : Callback> {
53 | override fun onResponse(call: Call>, response: Response>) {
54 | trySend(response.body()!!)
55 | close()
56 | }
57 |
58 | override fun onFailure(call: Call>, error: Throwable) {
59 | cancel(CancellationException(error))
60 | }
61 | })
62 | awaitClose()
63 | }
64 | val scope = CoroutineScope(EmptyCoroutineContext)
65 | scope.launch {
66 | flow9.collect {
67 | println("channelFlow with callback: $it")
68 | }
69 | /*flow8.collect {
70 | println("channelFlow: $it")
71 | }*/
72 | /*flow5.collect {
73 | println("Flow6 - 1: $it")
74 | }*/
75 | }
76 | scope.launch {
77 | /*flow5.collect {
78 | println("Flow6 - 2: $it")
79 | }*/
80 | }
81 | /*channel.send(1)
82 | channel.send(2)
83 | channel.send(3)
84 | channel.send(4)*/
85 | delay(10000)
86 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_4_flow/9_collect.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._4_flow
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.flow.collectIndexed
7 | import kotlinx.coroutines.flow.collectLatest
8 | import kotlinx.coroutines.flow.flow
9 | import kotlinx.coroutines.flow.flowOn
10 | import kotlinx.coroutines.flow.launchIn
11 | import kotlinx.coroutines.flow.onEach
12 | import kotlinx.coroutines.launch
13 | import kotlinx.coroutines.runBlocking
14 | import kotlinx.coroutines.withContext
15 | import kotlin.coroutines.EmptyCoroutineContext
16 |
17 | fun main() = runBlocking {
18 | val flow = flow {
19 | /*launch(Dispatchers.IO) {
20 | delay(2000)
21 | emit(2)
22 | }*/
23 | delay(1000)
24 | emit(1)
25 | }
26 | val scope = CoroutineScope(EmptyCoroutineContext)
27 |
28 | flow.onEach {
29 | println("flow: $it")
30 | }.launchIn(scope)
31 |
32 | scope.launch(Dispatchers.Default) {
33 | flow.collect {
34 | println("flow: $it")
35 | }
36 | flow.collectIndexed { index, value -> }
37 | flow.collectLatest { }
38 | }
39 | delay(10000)
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_5_collaboration/1_collaboration.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._5_collaboration
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.async
5 | import kotlinx.coroutines.channels.Channel
6 | import kotlinx.coroutines.delay
7 | import kotlinx.coroutines.launch
8 | import kotlinx.coroutines.runBlocking
9 | import java.util.concurrent.CountDownLatch
10 | import kotlin.concurrent.thread
11 | import kotlin.coroutines.EmptyCoroutineContext
12 |
13 | fun main() = runBlocking {
14 | val scope = CoroutineScope(EmptyCoroutineContext)
15 | val latch = CountDownLatch(2)
16 | thread {
17 | latch.await()
18 | //
19 | }
20 | thread {
21 | Thread.sleep(1000)
22 | latch.countDown()
23 | }
24 | thread {
25 | Thread.sleep(2000)
26 | latch.countDown()
27 | }
28 | val preJob1 = launch {
29 | delay(1000)
30 | }
31 | val preJob2 = launch {
32 | delay(2000)
33 | }
34 | launch {
35 | preJob1.join()
36 | preJob2.join()
37 | }
38 | val channel = Channel(2)
39 | launch {
40 | repeat(2) {
41 | channel.receive()
42 | }
43 | }
44 | launch {
45 | delay(1000)
46 | channel.send(Unit)
47 | }
48 | launch {
49 | delay(2000)
50 | channel.send(Unit)
51 | }
52 | val job1 = scope.launch {
53 | println("Job 1 started")
54 | delay(2000)
55 | println("Job 1 done")
56 | }
57 | val deferred = scope.async {
58 | println("Deferred started")
59 | delay(3000)
60 | println("Deferred done")
61 | "rengwuxian"
62 | }
63 | scope.launch {
64 | println("Job 2 started")
65 | delay(1000)
66 | job1.join()
67 | println("Deferred result: ${deferred.await()}")
68 | println("Job 2 done")
69 | }
70 | delay(10000)
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_5_collaboration/2_select.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._5_collaboration
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.async
6 | import kotlinx.coroutines.channels.Channel
7 | import kotlinx.coroutines.delay
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.runBlocking
10 | import kotlinx.coroutines.selects.onTimeout
11 | import kotlinx.coroutines.selects.select
12 | import kotlin.coroutines.EmptyCoroutineContext
13 | import kotlin.time.Duration.Companion.seconds
14 |
15 | @OptIn(ExperimentalCoroutinesApi::class)
16 | fun main() = runBlocking {
17 | val scope = CoroutineScope(EmptyCoroutineContext)
18 | val job1 = launch {
19 | delay(1000)
20 | println("job1 done")
21 | }
22 | val job2 = launch {
23 | delay(2000)
24 | println("job2 done")
25 | }
26 | val deferred = async {
27 | delay(500)
28 | "rengwuxian"
29 | }
30 | val channel = Channel()
31 | scope.launch {
32 | val result = select {
33 | job1.onJoin {
34 | delay(2000)
35 | 1
36 | }
37 | job2.onJoin {
38 | 2
39 | }
40 | deferred.onAwait {
41 | it
42 | }
43 | channel.onSend("haha") {
44 |
45 | }
46 | /* channel 的 onSend()、onReceive()、onReceiveCatching() 只能选其一,不能同时应用
47 | channel.onReceive {
48 |
49 | }
50 | channel.onReceiveCatching {
51 |
52 | }
53 | */
54 | onTimeout(1.seconds) {
55 |
56 | }
57 | }
58 | println("Result: $result")
59 | }
60 | delay(10000)
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_5_collaboration/3_mutex.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._5_collaboration
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.delay
5 | import kotlinx.coroutines.launch
6 | import kotlinx.coroutines.runBlocking
7 | import kotlinx.coroutines.sync.Mutex
8 | import kotlinx.coroutines.sync.Semaphore
9 | import kotlinx.coroutines.sync.withLock
10 | import java.util.concurrent.CopyOnWriteArrayList
11 | import java.util.concurrent.atomic.AtomicInteger
12 | import kotlin.concurrent.thread
13 | import kotlin.coroutines.EmptyCoroutineContext
14 |
15 | //@Synchronized
16 | fun main() = runBlocking {
17 | var number = 0
18 | val lock = Any()
19 | /*val thread1 = thread {
20 | repeat(100_0000) {
21 | synchronized(lock) {
22 | number++
23 | }
24 | }
25 | }
26 | val thread2 = thread {
27 | repeat(100_0000) {
28 | synchronized(lock) {
29 | number--
30 | }
31 | }
32 | }
33 | thread1.join()
34 | thread2.join()
35 | println("number: $number")*/
36 | val scope = CoroutineScope(EmptyCoroutineContext)
37 | val mutex = Mutex() // mutual exclusion
38 | val semaphore = Semaphore(3)
39 | AtomicInteger()
40 | CopyOnWriteArrayList()
41 | val job1 = scope.launch {
42 | semaphore.acquire()
43 | semaphore.release()
44 | repeat(100_0000) {
45 | mutex.withLock {
46 | number++
47 | }
48 | }
49 | }
50 | val job2 = scope.launch {
51 | repeat(100_0000) {
52 | mutex.withLock {
53 | number--
54 | }
55 | }
56 | }
57 | job1.join()
58 | job2.join()
59 | println("number: $number")
60 | delay(10000)
61 | }
62 |
63 | @Volatile var v = 0
64 | @Transient var t = 0
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_5_collaboration/4_ThreadLocal.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._5_collaboration
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.asContextElement
5 | import kotlinx.coroutines.delay
6 | import kotlinx.coroutines.launch
7 | import kotlinx.coroutines.runBlocking
8 | import kotlinx.coroutines.withContext
9 | import kotlin.coroutines.EmptyCoroutineContext
10 |
11 | val kotlinLocalString = ThreadLocal()
12 |
13 | fun main() = runBlocking {
14 | val scope = CoroutineScope(EmptyCoroutineContext)
15 | scope.launch {
16 | val stringContext = kotlinLocalString.asContextElement("rengwuxian")
17 | withContext(stringContext) {
18 | delay(100)
19 | kotlinLocalString.get()
20 | }
21 | }
22 | delay(10000)
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/_5_collaboration/Java.java:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines._5_collaboration;
2 |
3 | import java.util.concurrent.Semaphore;
4 | import java.util.concurrent.locks.ReentrantLock;
5 |
6 | public class Java {
7 | int number = 0;
8 | // final Object lock = new Object();
9 | ReentrantLock lock = new ReentrantLock();
10 | Semaphore semaphore = new Semaphore(3);
11 | Object waiter = new Object();
12 | volatile int v = 0;
13 | transient int t = 0;
14 |
15 | synchronized void synchronizedMethod() {
16 | try {
17 | waiter.wait();
18 | waiter.notify();
19 | waiter.notifyAll();
20 | } catch (InterruptedException e) {
21 | throw new RuntimeException(e);
22 | }
23 | try {
24 | semaphore.acquire();
25 | } catch (InterruptedException e) {
26 | throw new RuntimeException(e);
27 | } finally {
28 | semaphore.release();
29 | }
30 | lock.lock();
31 | try {
32 | lock.lock();
33 | try {
34 | number++;
35 | } finally {
36 | lock.unlock();
37 | }
38 | } finally {
39 | lock.unlock();
40 | }
41 | }
42 |
43 | void synchronizedMethod2() {
44 | synchronized (this) {
45 | number--;
46 | }
47 | }
48 |
49 | ThreadLocal localString = new ThreadLocal<>();
50 | String directString;
51 |
52 | void threadLocal() {
53 | String string = localString.get();
54 | localString.set("rengwuxian");
55 | String string1 = directString;
56 | directString = "扔物线";
57 |
58 | new Thread() {
59 | @Override
60 | public void run() {
61 | String innerString = localString.get();
62 | }
63 | }.start();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/common/Api.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines.common
2 |
3 | import io.reactivex.rxjava3.core.Single
4 | import retrofit2.Call
5 | import retrofit2.http.GET
6 | import retrofit2.http.Path
7 | import retrofit2.mock.BehaviorDelegate
8 | import java.util.concurrent.CompletableFuture
9 |
10 | const val GITHUB_API: String = "https://api.github.com"
11 |
12 | data class Contributor(val login: String, val contributions: Int)
13 |
14 | interface GitHub {
15 | // https://api.github.com/repos/{owner}/{repo}/contributors
16 | @GET("/repos/{owner}/{repo}/contributors")
17 | fun contributorsCall(
18 | @Path("owner") owner: String, // square
19 | @Path("repo") repo: String, // retrofit
20 | ): Call>
21 |
22 | @GET("/repos/{owner}/{repo}/contributors")
23 | suspend fun contributors(
24 | @Path("owner") owner: String,
25 | @Path("repo") repo: String,
26 | ): List
27 |
28 | @GET("/repos/{owner}/{repo}/contributors")
29 | fun contributorsFuture(
30 | @Path("owner") owner: String,
31 | @Path("repo") repo: String,
32 | ): CompletableFuture>
33 |
34 | @GET("/repos/{owner}/{repo}/contributors")
35 | fun contributorsRx(
36 | @Path("owner") owner: String,
37 | @Path("repo") repo: String,
38 | ): Single>
39 | }
40 |
41 | class MockGitHub(private val delegate: BehaviorDelegate) : GitHub {
42 | private val ownerRepoContributors: MutableMap>> =
43 | LinkedHashMap()
44 |
45 | init {
46 | // Seed some mock data.
47 | addContributor("square", "retrofit", "John Doe", 12)
48 | addContributor("square", "retrofit", "Bob Smith", 2)
49 | addContributor("square", "retrofit", "Big Bird", 40)
50 | addContributor("square", "okhttp", "Proposition Joe", 39)
51 | addContributor("square", "okhttp", "Keiser Soze", 152)
52 | }
53 |
54 | override fun contributorsCall(owner: String, repo: String): Call> {
55 | var response: List? = emptyList()
56 | val repoContributors: Map>? =
57 | ownerRepoContributors[owner]
58 | if (repoContributors != null) {
59 | val contributors: List? =
60 | repoContributors[repo]
61 | if (contributors != null) {
62 | response = contributors
63 | }
64 | }
65 | return delegate.returningResponse(response).contributorsCall(owner, repo)
66 | }
67 |
68 | override fun contributorsFuture(owner: String, repo: String): CompletableFuture> {
69 | var response: List? = emptyList()
70 | val repoContributors: Map>? =
71 | ownerRepoContributors[owner]
72 | if (repoContributors != null) {
73 | val contributors: List? =
74 | repoContributors[repo]
75 | if (contributors != null) {
76 | response = contributors
77 | }
78 | }
79 | return delegate.returningResponse(response).contributorsFuture(owner, repo)
80 | }
81 |
82 | override suspend fun contributors(owner: String, repo: String): List {
83 | var response: List? = emptyList()
84 | val repoContributors: Map>? =
85 | ownerRepoContributors[owner]
86 | if (repoContributors != null) {
87 | val contributors: List? =
88 | repoContributors[repo]
89 | if (contributors != null) {
90 | response = contributors
91 | }
92 | }
93 | return delegate.returningResponse(response).contributors(owner, repo)
94 | }
95 |
96 | override fun contributorsRx(owner: String, repo: String): Single> {
97 | var response: List? = emptyList()
98 | val repoContributors: Map>? =
99 | ownerRepoContributors[owner]
100 | if (repoContributors != null) {
101 | val contributors: List? =
102 | repoContributors[repo]
103 | if (contributors != null) {
104 | response = contributors
105 | }
106 | }
107 | return delegate.returningResponse(response).contributorsRx(owner, repo)
108 | }
109 |
110 | private fun addContributor(owner: String, repo: String, name: String?, contributions: Int) {
111 | var repoContributors = ownerRepoContributors[owner]
112 | if (repoContributors == null) {
113 | repoContributors = LinkedHashMap()
114 | ownerRepoContributors[owner] = repoContributors
115 | }
116 | var contributors = repoContributors[repo]
117 | if (contributors == null) {
118 | contributors = ArrayList()
119 | repoContributors[repo] = contributors
120 | }
121 | contributors.add(Contributor(name!!, contributions))
122 | }
123 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/common/Common.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines.common
2 |
3 | import retrofit2.Retrofit
4 | import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
5 | import retrofit2.converter.gson.GsonConverterFactory
6 | import retrofit2.mock.MockRetrofit
7 | import retrofit2.mock.NetworkBehavior
8 | import java.util.concurrent.TimeoutException
9 |
10 | private val retrofit =
11 | Retrofit.Builder().baseUrl(GITHUB_API)
12 | .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
13 | .addConverterFactory(GsonConverterFactory.create())
14 | .build()
15 | //val gitHub: GitHub = retrofit.create(GitHub::class.java
16 |
17 | // Create a MockRetrofit object with a NetworkBehavior which manages the fake behavior of calls.
18 | private val behavior = NetworkBehavior.create()
19 | private val mockRetrofit = MockRetrofit.Builder(retrofit).networkBehavior(behavior).build()
20 | private val delegate = mockRetrofit.create(GitHub::class.java)
21 | val gitHub: GitHub = MockGitHub(delegate)
22 | private val unstableBehavior = NetworkBehavior.create().apply {
23 | setFailurePercent(40)
24 | setFailureException(TimeoutException("Connection time out!"))
25 | }
26 | private val mockUnstableRetrofit = MockRetrofit.Builder(retrofit).networkBehavior(unstableBehavior).build()
27 | private val unstableDelegate = mockUnstableRetrofit.create(GitHub::class.java)
28 | val unstableGitHub: GitHub = MockGitHub(unstableDelegate)
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.platform.LocalContext
13 |
14 | private val DarkColorScheme = darkColorScheme(
15 | primary = Purple80,
16 | secondary = PurpleGrey80,
17 | tertiary = Pink80
18 | )
19 |
20 | private val LightColorScheme = lightColorScheme(
21 | primary = Purple40,
22 | secondary = PurpleGrey40,
23 | tertiary = Pink40
24 |
25 | /* Other default colors to override
26 | background = Color(0xFFFFFBFE),
27 | surface = Color(0xFFFFFBFE),
28 | onPrimary = Color.White,
29 | onSecondary = Color.White,
30 | onTertiary = Color.White,
31 | onBackground = Color(0xFF1C1B1F),
32 | onSurface = Color(0xFF1C1B1F),
33 | */
34 | )
35 |
36 | @Composable
37 | fun CourseCoroutinesTheme(
38 | darkTheme: Boolean = isSystemInDarkTheme(),
39 | // Dynamic color is available on Android 12+
40 | dynamicColor: Boolean = true,
41 | content: @Composable () -> Unit
42 | ) {
43 | val colorScheme = when {
44 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
45 | val context = LocalContext.current
46 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
47 | }
48 |
49 | darkTheme -> DarkColorScheme
50 | else -> LightColorScheme
51 | }
52 |
53 | MaterialTheme(
54 | colorScheme = colorScheme,
55 | typography = Typography,
56 | content = content
57 | )
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rengwuxian/coursecoroutines/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CourseCoroutines
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/com/rengwuxian/coursecoroutines/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.rengwuxian.coursecoroutines
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.jetbrains.kotlin.android) apply false
5 | alias(libs.plugins.compose.compiler) apply false
6 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. For more details, visit
12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.5.2"
3 | kotlin = "2.0.0"
4 | coreKtx = "1.13.1"
5 | fragment = "1.8.2"
6 | junit = "4.13.2"
7 | junitVersion = "1.2.1"
8 | espressoCore = "3.6.1"
9 | lifecycleRuntimeKtx = "2.8.4"
10 | activityCompose = "1.9.1"
11 | composeBom = "2024.08.00"
12 | coroutines = "1.9.0-RC"
13 | retrofit = "2.11.0"
14 | rxjava = "3.1.8"
15 | rxandroid = "3.0.2"
16 |
17 | [libraries]
18 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
19 | junit = { group = "junit", name = "junit", version.ref = "junit" }
20 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
21 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
22 | androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
23 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
24 | androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleRuntimeKtx" }
25 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
26 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
27 | androidx-ui = { group = "androidx.compose.ui", name = "ui" }
28 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
29 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
30 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
31 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
32 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
33 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
34 | coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
35 | coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
36 | retrofit-lib = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
37 | retrofit-mock = { group = "com.squareup.retrofit2", name = "retrofit-mock", version.ref = "retrofit" }
38 | retrofit-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
39 | retrofit-rxjava = { group = "com.squareup.retrofit2", name = "adapter-rxjava3", version.ref = "retrofit" }
40 | rxjava-core = { group = "io.reactivex.rxjava3", name = "rxjava", version.ref = "rxjava" }
41 | rxjava-android = { group = "io.reactivex.rxjava3", name = "rxandroid", version.ref = "rxandroid" }
42 |
43 | [plugins]
44 | android-application = { id = "com.android.application", version.ref = "agp" }
45 | jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
46 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 24 21:53:00 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/images/course-cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rengwuxian/CourseCoroutines/d0f82c3aa191311c5717258da24fb2d0e0cac5ad/images/course-cover.jpg
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google {
4 | content {
5 | includeGroupByRegex("com\\.android.*")
6 | includeGroupByRegex("com\\.google.*")
7 | includeGroupByRegex("androidx.*")
8 | }
9 | }
10 | mavenCentral()
11 | gradlePluginPortal()
12 | }
13 | }
14 | dependencyResolutionManagement {
15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | rootProject.name = "CourseCoroutines"
23 | include(":app")
24 |
--------------------------------------------------------------------------------