๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Android

[Kotlin] Coroutines - LifecycleScope

interfacer_han 2024. 2. 21. 12:34

๋ณธ ๊ฒŒ์‹œ๊ธ€์˜ Coroutine ๊ฐœ๋…์€ Android ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ์ „์ œ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค.

 

#1 ์ด์ „ ๊ฒŒ์‹œ๊ธ€

 

[Kotlin] Coroutines - ViewModelScope

๋ณธ ๊ฒŒ์‹œ๊ธ€์˜ Coroutine ๊ฐœ๋…์€ Android ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ์ „์ œ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค. #1 ViewModel ์† ์ „ํ†ต์ ์ธ ๋ฐฉ์‹์˜ Coroutinesimport androidx.lifecycle.ViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.D

kenel.tistory.com

์ด์ „ ๊ฒŒ์‹œ๊ธ€์—์„œ ViewModel์˜ ์ˆ˜๋ช…์ฃผ๊ธฐ๋ฅผ ์ฐธ์กฐํ•ด, ViewModel์ด ์†Œ๋ฉธ๋  ๋•Œ ์•Œ์•„์„œ ์ข…๋ฃŒ๋˜๋Š” CoroutineScope์— ๋Œ€ํ•ด ๋‹ค๋ค˜๋‹ค. ์ด๋ฒˆ์—” ๊ทธ ์ฐธ์กฐ์˜ ๋Œ€์ƒ์ด LifecycleOwner์ธ ๋ฒ„์ „์„ ์‚ดํŽด๋ณธ๋‹ค. ์ด์ „ ๊ธ€๊ณผ ๊ฑฐ์˜ ๋น„์Šทํ•œ ๊ฐœ๋…์ด๋ฏ€๋กœ, ๋ณธ ๊ฒŒ์‹œ๊ธ€์—์„  ์ด์ „ ๊ฒŒ์‹œ๊ธ€๊ณผ ์ค‘๋ณต๋˜๋Š” ์„ค๋ช…์€ ์ตœ์†Œํ•œ์œผ๋กœ ์ค„์˜€๋‹ค.

 

#2 lifecycle-run-ktx ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

#2-1 ๊ฐœ์š”

 

์ˆ˜๋ช… ์ฃผ๊ธฐ ์ธ์‹ ๊ตฌ์„ฑ์š”์†Œ๋กœ Kotlin ์ฝ”๋ฃจํ‹ด ์‚ฌ์šฉ  |  Android ๊ฐœ๋ฐœ์ž  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ธ์‹ ๊ตฌ์„ฑ์š”์†Œ๋กœ Kotlin ์ฝ”๋ฃจํ‹ด ์‚ฌ์šฉ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. Kotlin ์ฝ”

developer.android.com

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ LifecycleScope๋ฅผ ์ด์šฉํ•ด์„œ ์ƒ์šฉ๊ตฌ ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.

 

#2-2 androidx.lifecycle.LifecycleOwner.kt ์‚ดํŽด๋ณด๊ธฐ (LifecycleScope์™€ lifecycleScope)

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.lifecycle

import kotlinx.coroutines.CoroutineScope

/**
 * A class that has an Android lifecycle. These events can be used by custom components to
 * handle lifecycle changes without implementing any code inside the Activity or the Fragment.
 *
 * @see Lifecycle
 * @see ViewTreeLifecycleOwner
 */
public interface LifecycleOwner {
    /**
     * Returns the Lifecycle of the provider.
     *
     * @return The lifecycle of the provider.
     */
    public val lifecycle: Lifecycle
}

/**
 * [CoroutineScope] tied to this [LifecycleOwner]'s [Lifecycle].
 *
 * This scope will be cancelled when the [Lifecycle] is destroyed.
 *
 * This scope is bound to
 * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].
 */
public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope

LifecycleScope๋Š” LifecycleOwner ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„ ๊ฐ์ฒด์— ์ข…์†๋˜์–ด, ํ•ด๋‹น ๊ฐ์ฒด๋“ค์ด ์†Œ๋ฉธํ•  ๋•Œ ์•Œ์•„์„œ ์ข…๋ฃŒ๋˜๋Š” CoroutineScope๋‹ค. #3-1์—์„œ์ฒ˜๋Ÿผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ์ˆœ๊ฐ„๋ถ€ํ„ฐ LifecycleScope๋Š” LifecycleOwner์˜ ํ”„๋กœํผํ‹ฐ๋กœ์„œ ์ž๋™ ์ƒ์„ฑ๋œ๋‹ค. ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ์˜ ์ด๋ฆ„์€ ์ฒซ ๊ธ€์ž๊ฐ€ ์†Œ๋ฌธ์ž์ธ, lifecycleScope๋‹ค. lifecycleScope๋Š” coroutineScope์™€ ๋น„์Šทํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

#3 LifecycleScope ์‚ฌ์šฉํ•˜๊ธฐ

#3-1 build.gradle.kts (Module)์—์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‹ค์šด๋กœ๋“œ

plugins {
    ...
}

android {
    ...
}

dependencies {
    ...

    // LifecycleScope
    val lifecycle_version = "2.6.2"
    implementation ("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")
}

์—ฌ๊ธฐ์˜ ๊ตฌ๋ฌธ์„ ๋ณต์‚ฌํ•ด ๋ชจ๋“ˆ ์ˆ˜์ค€ ๊ทธ๋ž˜๋“ค ํŒŒ์ผ์— ๋ถ™์—ฌ๋„ฃ๋Š”๋‹ค.

 

#3-2 MainActivity.kt

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        lifecycleScope.launch {
            while (true) {
                Log.i("interfacer_han", "I'm Coroutines")
                delay(1000)
            }
        }
    }
}

์œ„ ์ฝ”๋“œ์˜ ์ฝ”๋ฃจํ‹ด์€ ํ•ด๋‹น Activity๊ฐ€ onDestory()๋˜๋ฉด ์•Œ์•„์„œ ์ข…๋ฃŒ๋œ๋‹ค.

 

Fragment๋„ Activity์ฒ˜๋Ÿผ LifecycleOwner์˜ ๊ตฌํ˜„์ฒด์ด๋ฏ€๋กœ, lifecycleScope๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ViewModel๋„ ๋ทฐ๋ชจ๋ธ ๋งŒ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์žˆ์ง€๋งŒ LifecycleOwner ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋Š” ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ViewModel์€ lifecycleScope ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ง€์ง€ ์•Š๊ณ , ๋”ฐ๋ผ์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์—†๋‹ค. ๋Œ€์‹  ViewModel์„ ์œ„ํ•œ ViewModelScope๊ฐ€ ์žˆ๋‹ค.

 

#3-3 repeatOnLifecycle()

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        Log.i("interfacer_han", "MainActivity.onCreate()")
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                Log.i("interfacer_han", "(lifecycleScope) repeat when onStart()")
            }
        }
    }

    override fun onStart() {
        Log.i("interfacer_han", "MainActivity.onStart()")
        super.onStart()
    }

    override fun onResume() {
        Log.i("interfacer_han", "MainActivity.onResume()")
        super.onResume()
    }

    override fun onPause() {
        Log.i("interfacer_han", "MainActivity.onPause()")
        super.onPause()
    }

    override fun onStop() {
        Log.i("interfacer_han", "MainActivity.onStop()")
        super.onStop()
    }

    override fun onRestart() {
        Log.i("interfacer_han", "MainActivity.onRestart()")
        super.onRestart()
    }

    override fun onDestroy() {
        Log.i("interfacer_han", "MainActivity.onDestroy()")
        super.onDestroy()
    }
}

/* Log ์ถœ๋ ฅ ๊ฒฐ๊ณผ - ์•ฑ ์ตœ์ดˆ ์‹คํ–‰ ์‹œ
MainActivity.onCreate()
MainActivity.onStart()
(lifecycleScope) repeat when onStart()
MainActivity.onResume()
*/

/* Log ์ถœ๋ ฅ ๊ฒฐ๊ณผ - ์•ฑ์„ ๋ฒ—์–ด๋‚ฌ์„ ๋•Œ
MainActivity.onPause()
MainActivity.onStop()
*/

/* Log ์ถœ๋ ฅ ๊ฒฐ๊ณผ - ์•ฑ์— ์žฌ์ง„์ž…ํ–ˆ์„ ๋•Œ
MainActivity.onRestart()
MainActivity.onStart()
(lifecycleScope) repeat when onStart()
MainActivity.onResume()
*/

์œ„ ์ฝ”๋“œ๋Š” ์ด ๊ฒŒ์‹œ๊ธ€์˜ #3-1์„ ์ฐธ์กฐํ•ด ์งฐ๋‹ค. LifecycleOwner์—์„œ ์“ฐ์ด๋Š” ์ฝ”๋ฃจํ‹ด์ด ์ƒ๋ช…์ฃผ๊ธฐ์— ์ข€ ๋” ๋””ํ…Œ์ผํ•˜๊ฒŒ ๋ฐ˜์‘ํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿด ๋•, repeatOnLifecycle()์„ ์‚ฌ์šฉํ•œ๋‹ค. ์–ด์ฉŒ๋ฉด ์ด repeatOnLifecycle()์ด์•ผ๋ง๋กœ LifecycleScope์˜ ์ง„๊ฐ€๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด LifecycleOwner ์ƒ๋ช…์ฃผ๊ธฐ์˜ ๊ฐ ๋‹จ๊ณ„์—์„œ ์‹œ์ž‘๋  ์ฝ”๋ฃจํ‹ด์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. (์ข…๋ฃŒ๋Š” ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ LifecycleOwner๊ฐ€ ํŒŒ๊ดด๋  ๋•Œ ์ˆ˜ํ–‰๋œ๋‹ค). ์œ„์˜ ์ฝ”๋“œ์—์„  Lifecycle.State๋ฅผ Started๋กœ ์„ค์ •ํ–ˆ์œผ๋ฏ€๋กœ, onStart()์ผ ๋•Œ ์ˆ˜ํ–‰๋œ๋‹ค. ์ด์ฒ˜๋Ÿผ repeateOnLifecycle()๊ฐ€ ์“ฐ์ด๋ฉด ์ข€ ๋” ์ข์€ ๋ฒ”์œ„, ๊ตญ์†Œ์ ์ธ ๋ฒ„์ „์˜ lifecycleScope๊ฐ€ ๋œ๋‹ค.

 

#3-4 repeatOnLifecycle()์˜ ์กด์žฌ ์ด์œ  ์ถ”์ธก

ํ•˜์ง€๋งŒ, #3-3์—์„  ์˜๋ฌธ์ ์ด ํ•˜๋‚˜ ์ƒ๊ธด๋‹ค. ๊ตณ์ด repeatOnLifecycle()๋ฅผ ์“ธ ๊ฒŒ ์•„๋‹ˆ๋ผ, ๊ทธ๋ƒฅ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์“ฐ๋ฉด ๊ทธ๋งŒ ์•„๋‹Œ๊ฐ€?

...

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        Log.i("interfacer_han", "MainActivity.onCreate()")
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    
    ...

    override fun onStart() {
        Log.i("interfacer_han", "MainActivity.onStart()")
        super.onStart()

        lifecycleScope.launch {
            Log.i("interfacer_han", "(lifecycleScope) repeat when onStart()")
        }
    }

    ...
}

/* Log ์ถœ๋ ฅ ๊ฒฐ๊ณผ - ์•ฑ ์ตœ์ดˆ ์‹คํ–‰ ์‹œ
MainActivity.onCreate()
MainActivity.onStart()
(lifecycleScope) repeat when onStart()
MainActivity.onResume()
*/

/* Log ์ถœ๋ ฅ ๊ฒฐ๊ณผ - ์•ฑ์„ ๋ฒ—์–ด๋‚ฌ์„ ๋•Œ
MainActivity.onPause()
MainActivity.onStop()
*/

/* Log ์ถœ๋ ฅ ๊ฒฐ๊ณผ - ์•ฑ์— ์žฌ์ง„์ž…ํ–ˆ์„ ๋•Œ
MainActivity.onRestart()
MainActivity.onStart()
(lifecycleScope) repeat when onStart()
MainActivity.onResume()
*/

์ถœ๋ ฅ ๊ฒฐ๊ณผ๊ฐ€ #3-3๊ณผ ๋˜‘๊ฐ™๋‹ค. ๊ฐ™์€ ์ฝ”๋“œ๋ผ๋Š” ๋ง์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด repeatOnLifecycle()๊ฐ€ ๊ตณ์ด ์กด์žฌํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€? ์‚ฌ์‹ค ๋‚˜๋„ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์•„๋งˆ๋„, ๊ฐ Lifecycle.State์— ์–ด๋–ค ์ฝ”๋ฃจํ‹ด์ด ์ˆ˜ํ–‰๋  ์ง€ (๊ฐ€๋…์„ฑ ์ข‹๊ฒŒ) ๋ชจ์•„์„œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์œผ๋กœ ์ถ”์ธก๋œ๋‹ค.

 

#4 ์š”์•ฝ

LifecycleOwner์—์„œ์˜ CoroutineScope ์ƒ์„ฑ ๋ฐ Cancellation์„ ์•”์‹œํ™”ํ•œ๋‹ค.

 

#5 ์™„์„ฑ๋œ ์•ฑ

 

android-practice/coroutines/LifecycleScope at master ยท Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com