├── .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 | ![](./images/course-cover.jpg) -------------------------------------------------------------------------------- /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 |