본 게시글의 Coroutine 개념은 Android 내에서 사용되는 것을 전제로 작성되었다.
#1 이전 게시글
이전 게시글에서 ViewModel의 수명주기를 참조해, ViewModel이 소멸될 때 알아서 종료되는 CoroutineScope에 대해 다뤘다. 이번엔 그 참조의 대상이 LifecycleOwner인 버전을 살펴본다. 이전 글과 거의 비슷한 개념이므로, 본 게시글에선 이전 게시글과 중복되는 설명은 최소한으로 줄였다.
#2 lifecycle-run-ktx 라이브러리
#2-1 개요
이 라이브러리의 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' 카테고리의 다른 글
[Android] Room - 기초, INSERT와 DELETE 연습 (0) | 2024.02.23 |
---|---|
[Kotlin] Coroutines - LiveData Builder (0) | 2024.02.22 |
[Android] RecyclerView - Adapter에 인자(Argument) 전달 (0) | 2024.02.03 |
[Android] RecyclerView - 기초 (0) | 2024.02.02 |
[Android] Navigation - 애니메이션 (1) | 2024.01.27 |