Upgrade to Pro — share decks privately, control downloads, hide ads and more …

코루틴 너 은행에서 뭐하니?

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

코루틴 너 은행에서 뭐하니?

드로이드 나이츠 2019에서 발표한
자료입니다.

Avatar for eastar Jeong

eastar Jeong

April 05, 2019

More Decks by eastar Jeong

Other Decks in Programming

Transcript

  1. 소개 • c 언어 개발자(임베디드, MFC, ActiveX) • 9년차 안드로이드

    개발자 • 하나은행에서 안드로이드 개발자로 근무 중 • 한 여인의 남편이자 두아이의 아빠
  2. 사용환경 In your project-level build.gradle buildscript { ext.kotlin_version = '1.3.21'

    } dependencies { classpath "com.android.tools.build:gradle:3.2+" … In your app-level build.gradle dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
  3. Coroutines Guide • Coroutine basics • Cancellation and timeouts •

    Composing suspending functions • Coroutine context and dispatchers • Exception handling and supervision • Channels (experimental) • Shared mutable state and concurrency • Select expression (experimental) https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html Select expressions are an experimental feature of kotlinx.coroutines. Their API is expected to evolve in the upcoming updates of the kotlinx.coroutines library with potentially breaking changes.
  4. Coroutines Guide • Coroutine basics • Cancellation and timeouts •

    Composing suspending functions • Coroutine context and dispatchers • Exception handling and supervision • Channels (experimental) • Shared mutable state and concurrency • Select expression (experimental) https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html
  5. Kotlin Coroutines을 시작하면 겪는 일 • Hello World • 학습팁

    1 - 실행 순서 추적하기 • 학습팁 2 - 실행 시간표시 • sample 그대로 했는데 안돼요~ (runBlocking)
  6. • Hello World • 학습팁 1 - 실행 순서 추적하기

    • 학습팁 2 - 실행 시간표시 • sample 그대로 했는데 안돼요~ (runBlocking) 내가 Kotlin Coroutines을 시작하면서 겪은 일
  7. fun main() = runBlocking { // this: CoroutineScope launch {

    delay(200L) println( "Task from runBlocking") } coroutineScope { // Creates a new coroutine scope launch { delay(500L) println("Task from nested launch") } delay(100L) println("Task from coroutine scope") // This line will be printed before nested launch } println("Coroutine scope is over") // This line is not printed until nested launch completes } println(Exception().stackTrace[0]) println(Exception().stackTrace[0]) println(Exception().stackTrace[0].toString() + "Task from runBlocking") println(Exception().stackTrace[0]) println(Exception().stackTrace[0]) println(Exception().stackTrace[0]) println(Exception().stackTrace[0]).toString()+"Task from nested launch") println(Exception().stackTrace[0]) println(Exception().stackTrace[0].toString()+"Task from coroutine scope") //This line will be printed before nested launch println(Exception().stackTrace[0].toString()+"Coroutine scope is over") //This line is not printed until nested launch completes 학습팁 1 - 실행 순서 추적하기
  8. fun main() = runBlocking { // this: CoroutineScope launch {

    delay(200L) println( "Task from runBlocking") } coroutineScope { // Creates a new coroutine scope launch { delay(500L) println("Task from nested launch") } delay(100L) println("Task from coroutine scope") // This line will be printed before nested launch } println("Coroutine scope is over") // This line is not printed until nested launch completes } println(Exception().stackTrace[0]) println(Exception().stackTrace[0]) println(Exception().stackTrace[0].toString() + "Task from runBlocking") println(Exception().stackTrace[0]) println(Exception().stackTrace[0]) println(Exception().stackTrace[0]) println(Exception().stackTrace[0]).toString()+"Task from nested launch") println(Exception().stackTrace[0]) println(Exception().stackTrace[0].toString()+"Task from coroutine scope") // This line will be printed before nested launch println(Exception().stackTrace[0].toString()+"Coroutine scope is over") // This line is not printed until nested launch completes 학습팁 1 - 실행 순서 추적하기 main$1.invokeSuspend(Scopebuilder.kt:5) main$1.invokeSuspend(Scopebuilder.kt:11) main$1$2.invokeSuspend(Scopebuilder.kt:14) main$1$2.invokeSuspend(Scopebuilder.kt:21) main$1$1.invokeSuspend(Scopebuilder.kt:7) main$1$2$1.invokeSuspend(Scopebuilder.kt:16) main$1$2.invokeSuspend(Scopebuilder.kt:24)Task from coroutine scope main$1$1.invokeSuspend(Scopebuilder.kt:9)Task from runBlocking main$1$2$1.invokeSuspend(Scopebuilder.kt:18) main$1$2$1.invokeSuspend(Scopebuilder.kt:19) main$1.invokeSuspend(Scopebuilder.kt:27)Coroutine scope is over
  9. fun main() = runBlocking { // this: CoroutineScope println(Exception().stackTrace[0]) launch

    { println(Exception().stackTrace[0]) delay(200L) println(Exception().stackTrace[0].toString() + "Task from runBlocking") } println(Exception().stackTrace[0]) coroutineScope { // Creates a new coroutine scope println(Exception().stackTrace[0]) launch { println(Exception().stackTrace[0]) delay(500L) println(println(Exception().stackTrace[0]).toString()+"Task from nested launch") } println(Exception().stackTrace[0]) delay(100L) println(Exception().stackTrace[0].toString()+"Task from coroutine scope") // This line will be printed before nested launch } println(Exception().stackTrace[0].toString()+"Coroutine scope is over") // This line is not printed until nested launch completes } 학습팁 1 - 실행 순서 추적하기(전체코드)
  10. var keep = 0L fun nano() = (if (keep ==

    0L) 0L else System.nanoTime() - keep) .also { keep = System.nanoTime() }.comma private val Long.comma: String get() = String.format(Locale.getDefault(), "%,25d", this) 학습팁 2 - 실행 시간표시
  11. fun main() = runBlocking { println(nano()) doSomethingUsefulOne() println(nano()) doSomethingUsefulTwo() println(nano())

    } 학습팁 2 - 실행 시간표시 0 1,043,623,656 1,000,831,383 Process finished with exit code 0
  12. fun main() = runBlocking { println(nano()) doSomethingUsefulOne() println(nano()) doSomethingUsefulTwo() println(nano())

    } suspend fun doSomethingUsefulOne(): Int { delay(1000L) // pretend we are doing something useful here return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000L) // pretend we are doing something useful here, too return 29 } var keep = 0L fun nano() = (if (keep == 0L) 0L else System.nanoTime() - keep).also { keep = System.nanoTime() }.comma private val Long.comma: String get() = String.format(Locale.getDefault(), "%,25d", this) 학습팁 2 - 실행 시간표시(전체 코드)
  13. etc • https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and- dispatchers.html#naming-coroutines-for-debugging fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")

    fun main() = runBlocking(CoroutineName("main")) { log("Started main coroutine") // run two background value computations val v1 = async(CoroutineName("v1coroutine")) { delay(500) log("Computing v1") 252 } ... } [main @main#1] Started main coroutine [main @v1coroutine#2] Computing v1 [main @v2coroutine#3] Computing v2 Process finished with exit code 0
  14. 이것만 알고시작하자 fun main() = CoroutineScope(Dispatchers.Main).launch { val result :

    Boolean = checkPermission() } suspend fun checkPermission() = suspendCancellableCoroutine<Boolean> { cancellableContinuation -> setOnSuccessListener { cancellableContinuation.resume(true) } setOnFaileListener { cancellableContinuation.cancel() } }
  15. Kotlin Coroutine 사용하기 • Java에서 사용 가능할까? • 계좌 사본

    다운로드 • Unit Test • 뱅크 시작 시 보안 처리 • 이체 완료 공유하기
  16. Java에서 사용 가능할까? I'd suggest to look at Kotlin coroutines

    on JVM. It falls into a different category, though. There is no byte-code manipulation involved and it works on Android, too. However, you will have to write your coroutines in Kotlin. The upside is that Kotlin is designed for interoperability with Java in mind, so you can still continue to use all your Java libraries and freely combine Kotlin and Java code in the same project, even putting them side-by-side in the same directories and packages. This Guide to kotlinx.coroutines provides many more examples, while the coroutines design document explains all the motivation, use-cases and implementation details. https://stackoverflow.com/a/43228137 Roman Elizarov
  17. 계좌 사본 다운로드 private fun setDownload(context: Context) { webview.setDownloadListener {

    url, userAgent, contentDisposition, mimetype, contentLength -> PermissionRequest.builder(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) .setDenyMessage("파일 저장 권한을 주셔야만 작업이 가능합니다.") .setOnPermissionGrantedListener { val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype) setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } } .run() } } Before 권한 권한 다운로드
  18. 계좌 사본 다운로드 Before 권한 private fun setDownload(context: Context) {

    webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> PermissionRequest.builder(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) .setDenyMessage("파일 저장 권한을 주셔야만 작업이 가능합니다.") .setOnPermissionGrantedListener { } .run() } } val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype) setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } 권한 다운로드 Callback hell
  19. 계좌 사본 다운로드 Before 권한 private fun setDownload(context: Context) {

    webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> PermissionRequest.builder(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) .setDenyMessage("파일 저장 권한을 주셔야만 작업이 가능합니다.") .setOnPermissionGrantedListener { } .run() } } val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype) setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } 권한 다운로드
  20. val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype) setDescription("Download

    file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } 계좌 사본 다운로드 Before 권한 private fun setDownload(context: Context) { webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> PermissionRequest.builder(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) .setDenyMessage("파일 저장 권한을 주셔야만 작업이 가능합니다.") .setOnPermissionGrantedListener { } .run() } } 다운로드 권한
  21. checkPermission() val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype)

    setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } 계좌 사본 다운로드 Before private fun setDownload(context: Context) { webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> } } 권한 다운로드
  22. 코루틴 checkPermission() val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply {

    setMimeType(mimetype) setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } 계좌 사본 다운로드 Before private fun setDownload(context: Context) { webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> } } 다운로드 CoroutineScope(Dispatchers.Main).launch { } 권한
  23. 코루틴 checkPermission() 계좌 사본 다운로드 Before private fun setDownload(context: Context)

    { webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> } } CoroutineScope(Dispatchers.Main).launch { } downloadStart(url, userAgent, contentDisposition, mimetype, contentLength) 권한 다운로드
  24. 코루틴 checkPermission() 계좌 사본 다운로드 private fun setDownload(context: Context) {

    webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> } } CoroutineScope(Dispatchers.Main).launch { } downloadStart(url, userAgent, contentDisposition, mimetype, contentLength) 권한 다운로드 After
  25. private fun setDownload() { webview.setDownloadListener { url, userAg... CoroutineScope(Dispatchers.Main).launch {

    checkPermission() downloadStart(url, userAg...) } } } 계좌 사본 다운로드 After
  26. private fun setDownload() { webview.setDownloadListener { url, userAg... CoroutineScope(Dispatchers.Main).launch {

    checkPermission() downloadStart(url, userAg...) } } } cancellableContinuation.resume(Unit) 계좌 사본 다운로드 After
  27. private fun setDownload() { webview.setDownloadListener { url, userAg... CoroutineScope(Dispatchers.Main).launch {

    checkPermission() downloadStart(url, userAg...) } } } 계좌 사본 다운로드 cancellableContinuation.cancel() After
  28. private suspend fun checkPermission() = suspendCancellableCoroutine<Unit> { cancellableContinuation -> PermissionRequest.builder(this,

    Manifest.permission.WRITE_EXTERNAL_STORAGE) .setOnPermissionGrantedListener { cancellableContinuation.resume(Unit) } .setOnPermissionDeniedListener { _, _ -> cancellableContinuation.cancel() } .run() } After 계좌 사본 다운로드
  29. 계좌 사본 다운로드 private fun setDownload(context: Context) { webview.setDownloadListener {

    url, userAgent, contentDisposition, mimetype, contentLength -> PermissionRequest.builder(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) .setDenyMessage("파일 저장 권한을 주셔야만 작업이 가능합니다.") .setOnPermissionGrantedListener { val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype) setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) } } .run() } } Before 권한 권한 다운로드
  30. private fun setDownload2(context: Context) { webview.setDownloadListener { url, userAgent, contentDisposition,

    mimetype, contentLength -> AlertDialog.Builder(context) .setMessage("다운로드 권한이 없습니다.") .setPositiveButton("다운로드") { _, _ -> PermissionRequest.builder(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) .setOnPermissionGrantedListener { val filename = URLUtil.guessFileName(url, contentDisposition, mimetype) DownloadManager.Request(Uri.parse(url)).apply { setMimeType(mimetype) setDescription("Download file...") setTitle(filename) allowScanningByMediaScanner() setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) }.also { request -> val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager dm?.enqueue(request) Toast.makeText(context, filename, Toast.LENGTH_LONG).show() } } .run() }.show() } } 계좌 사본 다운로드 Before 권한 다운로드 Dialog
  31. Unit Test fun asyndFun(callback: (Int) -> Unit) { Executors.newSingleThreadExecutor().execute{ println("

    work thread start") Thread.sleep(1000) callback(2 + 2) println(" work thread end") } }
  32. Unit Test case Bad 1 @Test fun `Bad case 1

    must fail`() { println("main thread start") asyndFun { println("main thread check assert") //이 로그는 확인 할 수가 없어요 assertEquals(3, it) } println("main thread end") } Bad
  33. Unit Test case Bad 1 @Test fun `Bad case 1

    must fail`() { println("main thread start") asyndFun { println("main thread check assert") //이 로그는 확인 할 수가 없어요 assertEquals(3, it) } println("main thread end") } Bad main thread start main thread end work thread start Process finished with exit code 0
  34. Unit Test case Bad 2 @Test fun `Bad case 2

    must success`() { println("main thread start") var result = -1 asyndFun { println("main thread check assert") //이 로그는 확인 할 수가 없어요 result = it } assertEquals(4, result) println("main thread end") } Bad
  35. Unit Test case Bad 2 @Test fun `Bad case 2

    must success`() { println("main thread start") var result = -1 asyndFun { println("main thread check assert") //이 로그는 확인 할 수가 없어요 result = it } assertEquals(4, result) println("main thread end") } Bad main thread start work thread start java.lang.AssertionError: Expected :4 Actual :-1
  36. Unit Test(Semaphore) Before @Test fun `use Semaphore를 이용한 동기화 사용`()

    { println("main thread start") val semaphore = Semaphore(0) var result = -1 asyncFun { result = it semaphore.release() } println("main thread check assert") semaphore.acquire() assert(4 == result) println("main thread end") }
  37. Unit Test(suspendCoroutine) @Test fun `use suspendCoroutine`() = runBlocking { println("main

    thread start") val result = suspendCoroutine<Int> { continuation -> asyncFun { continuation.resume(it) } } println("main thread check assert") assertEquals(4, result) println("main thread end") } After
  38. Unit Test(channel) @Test fun `use Channel final`() = runBlocking {

    println("main thread start") val channel = Channel<Int>() asyncFun { launch { channel.send(it) } } println("main thread check assert") val result = channel.receive() assertEquals(4, result) println("main thread end") } After main thread start main thread check assert work thread start work thread end main thread end
  39. 뱅크 시작 시 보안 처리 v3 S SSOCheck N E

    Rooting v3 필수권한 처리 최초실행 공지 초기설정 파일 통신장애 처리 무결성 검증 업데이트 버전 Deeplink처리 (중략) 공지 팝업 SSO rooting notice rootingCheck v3Check noticeCheck
  40. var rootingChecked = false var v3Checked = false fun load()

    { if (!rootingChecked) { step00RootingCheck() return } if (!v3Checked) { step01v3Check() return } 뱅크 시작 시 보안 처리 Before fun step00RootingCheck() { //doSomeThing rootingCheckedr = true load() } fun step01v3Check() { //doSomeThing v3Checked = true load() }
  41. fun load() = CoroutineScope(Dispatchers.Main).launch { step00RootingCheck() step01v3Check() val deferredGcm =

    async { step04gcmCheck() } val deferredBanner = async { step06assetBannerCheck() } step07v3UpdateCheck() step11noticeCheck() step12checkSSO() deferredGcm.await() deferredBanner.await() } After 뱅크 시작 시 보안 처리
  42. private suspend fun step00RootingCheck() = suspendCancellableCoroutine<Unit> { continuation -> if

    (IS_ROOTING) {// 루팅 또는 기타오류 val message = "루팅된 단말입니다. … 합니다." AlertDialog.Builder(this).setMessage(message) .setPositiveButton("종료", null) .setOnDismissListener { continuation.cancel() exit() } .show() return@suspendCancellableCoroutine } continuation.resume(Unit) } After 뱅크 시작 시 보안 처리(rooting check) suspendCancellable Coroutine isRooting == true cancel isRooting == false resume
  43. After 뱅크 시작 시 보안 처리(notice check) private suspend fun

    step11noticeCheck() = suspendCancellableCoroutine<String> { continuation -> if (HAS_EVENT) { continuation.resume("") return@suspendCancellableCoroutine } AlertDialog.Builder(this) .setPositiveButton("참여하기") { _, _ -> continuation.resume("https://event.bank.com") } .setNegativeButton("나중에 보기", null) .setOnDismissListener { if (!continuation.isCompleted) continuation.resume("") } .show() } suspendCancellable Coroutine resume resume event
  44. After 뱅크 시작 시 보안 처리(notice check) suspend fun step11noticeCheck()

    = suspendCancellableCoroutine<String> { co -> if (!HAS_EVENT) { co.resume("") return } AlertDialog.Builder(this) .setPositiveButton("참여하기") { _, _ -> co.cancel("http://") } .setNegativeButton("나중에 보기", null) .setOnDismissListener { co.resume("") } .show() return@suspendCancellableCoroutine } }
  45. fun load() = CoroutineScope(Dispatchers.Main).launch { step00RootingCheck() step01v3Check() val deferredGcm =

    async { step04gcmCheck() } val deferredBanner = async { step06assetBannerCheck() } step07v3UpdateCheck() step11noticeCheck() step12checkSSO() deferredGcm.await() deferredBanner.await() } After 뱅크 시작 시 보안 처리(전체코드) continuation->cancel async async wait continuation->resume
  46. fun dynamicLinks() = CoroutineScope(Dispatchers.Main).launch { try { showProgress() val builder

    = getDynamicLinkBuilder() setSharing(builder.buildDynamicLink().uri) val uri = getShortLink(builder) setSharing(uri) } catch (e: Exception) { e.printStackTrace() } finally { dismissProgress() } } After 이체 완료 공유하기
  47. private suspend fun getShortLink(builder: DynamicLink.Builder): Uri = suspendCancellableCoroutine { continuation

    -> builder.buildShortDynamicLink().apply { addOnSuccessListener { continuation.resume(it.shortLink) } addOnCanceledListener { continuation.cancel() } addOnFailureListener { continuation.resumeWithException(it) } continuation.invokeOnCancellation { Log.w(it) } //for log addOnCompleteListener { Log.w(it.exception?.printStackTrace()) if (it.isSuccessful) Log.i(it.result?.shortLink) else Log.w(it.isSuccessful) } } } After 이체 완료 공유하기
  48. Coroutine Support in ViewModels • Coroutine Support in ViewModels using

    the new ViewModelScope Extension Property ◦ Android Weekly #352 #354 ◦ ViewModelScope Extension Property ◦ https://developer.android.com/jetpack/androidx/releases/lifecycle#2.1.0-alpha01 • Sample ◦ https://craigrussell.io/2019/03/coroutine-support-in-viewmodels-using-the-new- viewmodelscope-extension-property ◦ https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#0
  49. Coroutine Support in ViewModels class LoginViewModel : ViewModel(), CoroutineScope {

    private val job = SupervisorJob() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main override fun onCleared() { super.onCleared() job.cancel() } fun onUserDoingAThing() { launch { //TODO } } }
  50. Coroutine Support in ViewModels class LoginViewModel : ViewModel() { fun

    onUserDoingAThing() { viewModelScope.launch { // get your work done } } }
  51. Coroutine Support in ViewModels class LoginViewModel : ViewModel() { fun

    onUserDoingAThing() { viewModelScope.launch(Dispatchers.IO) { // get your work done } } }
  52. Retrofit with Kotlin Coroutines • Add first-party Kotlin coroutine suspend

    support • 2.0.0-beta2(Feb 16, 2019) • https://zsmb.co/retrofit-meets-coroutines/ • https://medium.com/exploring-android/android-networking-with-coroutines- and-retrofit-a2f20dd40a83 • https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter
  53. Retrofit with Kotlin Coroutines 사용환경 repositories { maven { url

    'https://oss.sonatype.org/content/repositories/snapshots' } } dependencies { implementation 'com.squareup.retrofit2:retrofit:2.5.1-SNAPSHOT' implementation 'com.squareup.retrofit2:converter-gson:2.5.1-SNAPSHOT' }
  54. RxJava vs Kotlin Coroutines • Coroutines ◦ 비동기 처리 ◦

    Callback-based를 순차적 코드로 변환 ◦ 백그라운드 스레드 또는 AsyncTask(Android) 대체 • RxJava ◦ I'm still in java ◦ Stream ◦ 함수화 캡슐화
  55. • Demo ◦ https://coroutine-demo.eastar.dev/ • 공식 가이드 ◦ https://kotlinlang.org/docs/reference/coroutines/basics.html ◦

    https://medium.com/@myungpyo/reading-coroutine-official-guide-thoroughly-part-0-20176d431e9d ◦ https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/README.md • KotlinConf 2017 - Deep Dive into Coroutines on JVM by Roman Elizarov ◦ https://youtu.be/YrrUCSi72E8 • Kotlin의 Coroutine은 어떻게 동작하는가 ◦ https://medium.com/til-kotlin-ko/kotlin의-coroutine은-어떻게-동작하는가-789291da6a50 • Android Suspenders (Android Dev Summit '18) ◦ https://youtu.be/EOjq4OIWKqM • 코루틴 동작 설명 영상 ◦ https://youtu.be/EOjq4OIWKqM?t=877 • CallbackHell 영상 ◦ https://youtu.be/4W3ruTWUhpw?t=213 참고자료
  56. RxJava is like a bazooka and most applications don’t use

    half of its firepower. RxJava는 바주카포와 같으며 대부분의 응용 프로그램은 화력의 절반도 사용하지 않습니다. https://proandroiddev.com/i-exchanged-rxjava-for-coroutines-in-my-android-application-why-you-probably-should-do-the-same-5526dfb38d0e RxJava coroutines 출처 나무위키
  57. Android Weekly with Coroutine(1) #214 First glimpse of Kotlin 1.1:

    Coroutines, Type aliases and more #230 A glimpse of Async-Await on Android #246 kotlin-coroutines-retrofit #247 Kotlin 1.1 Released with JavaScript Support, Coroutines and more #249 Why Im Skeptical about Kotlin Coroutines #262 A first walk into Kotlin coroutines on Android #274 Rewriting RxJava with Kotlin Coroutines? #278 Create a Clean-Code App #279 Diving deep into Kotlin Coroutines #282 Clean App with Kotlin & Architecture Components - Part 2 #285 Clean-Code App with Kotlin and Architecture Components ? Part 3 #290 retrofit2-kotlin-coroutines-adapter #293 Simple asynchronous loading with Kotlin Coroutines #303 Android Coroutine Recipes #303 Kotlin coroutines vs RxJava: an initial performance test #304 Coroutines and RxJava???An Asynchronicity Comparison (Part 1)
  58. Android Weekly with Coroutine(1) #305 BleGattCoroutines #307 Coroutines and RxJava???An

    Asynchronicity Comparison (Part 4) #308 Even smarter async with coroutine actors #308 Kotlin Coroutines — Handling concurrency like a pro with Retrofit 2 #308 RxJava to Kotlin coroutines #309 Coroutines and RxJava? (Part 5): Operators #319 From Java to Kotlin beyond Alt+Shift+Cmd+K #320 Simple network calls using Retrofit, LiveData, Kotlin Coroutines and DSL #324 Forget RxJava: Kotlin Coroutines are all you need. Part 1/2 #325 A major release just around the corner ? meet Kotlin 1.3-M2 #325 Announcing Okio 2: Our fast + simple I/O library #325 I exchanged RxJava for coroutines in my Android application #329 Demystifying CoroutineContext #330 Unidirectional Data Flow using Coroutines #331 Explore How Kotlin Coroutine Works Under The Hood #331 Kotlin Coroutines Introduction and Guide
  59. Android Weekly with Coroutine(3) #332 Async Operations with Kotlin Coroutines

    ? Part 1 #332 kotlinx.coroutines 1.0.0-RC1 #333 Kotlin Coroutines in action #333 Part 2???Coroutine Cancellation and Structured Concurrency #334 Android Networking with Coroutines and Retrofit #334 Blocking threads, suspending coroutines #334 Kotlin 1.3 Released #334 Using Kotlin Coroutines in your Android App #335 Kotlin Coroutines Concurrency #336 Migrate your Android project to Kotlin 1.3 #337 Black Friday deals at raywenderlich.com #337 Playing With Kotlin Coroutines For Best Practices #339 Chris Banes: Android Suspenders #339 Kotlin Coroutines patterns & anti-patterns #339 Level Up in Kotlin #342 Coroutines and Idling Resources
  60. Android Weekly with Coroutine(1) #346 Kotlin: Diving in to Coroutines

    and Channels #347 Android Team/Tech Lead ? ?? ?? #347 Kotlin Coroutines Cheat Sheet #348 Room & Coroutines #349 Launching a Kotlin Coroutine for immediate execution #349 Retrofit meets coroutines #352 Coroutine Support in ViewModels #354 Easy Coroutines in Android: viewModelScope #355 Kotlin Coroutines: A Case Study, Part 1
  61. Q&A