[Android] Coroutine(코루틴)

plz_no_anr·2021년 10월 18일
1

Android

목록 보기
2/6
post-thumbnail

Coroutine하면 떠오르는 것.. 비동기
비동기에 대해 알려면 안드로이드의 스레드에 대해 먼저 이해해야 합니다.
안드로이드에는 기본적으로 두 가지의 스레드가 존재하는데
1. Main Thread(Ui Thread)
2. Worker Thread(Background Thread)

이렇게 두가지가 존재하는데 이중에서 Worker Thread는 여러 개가 존재할 수 있습니다.

안드로이드는 왜 여러 개의 스레드를 사용할까?🤔

예를 들어 우리가 카톡을 하며 영화를 다운 받는다고 가정했을 때 한 개의 Thread로 동작한다면 영화를 다운 받는 중에는 카톡을 할 수 없게 됩니다.
이는 Thread가 한 가지의 작업 밖에 처리할 수 없기 때문인데 이때 필요한 것이 멀티 스레딩입니다.
근데 여기서 안드로이드는 왜 꼭 Main Thread에서만 Ui작업을 해야 하도록 설계 했을까요??
그 이유는 만약 TextView의 텍스트를 변경하는 작업을 하는데 여러 Thread가 동시에 하나의 텍스트를 변경하려고 한다면 어떨까?? 당연히 Exception이 발생할 수 밖에 없겠죠.
이러한 이유 때문에 안드로이드에서는 Ui작업을 오직 Main Thread에서만 가능하도록 설계되었습니다.
또한 안드로이드 Main Thread가 일정 시간 어떤 Task에 붙잡혀 있으면 한번쯤은 보셨을 ANR(Application Not Responding)이 발생하게 됩니다.

그럼 안드로이드의 Thread구조는 알았고 Coroutine은 뭘까요..?


Coroutine을 시작하기전 자바로 입문한 나는 가장 먼저 접한 비동기 방식이 지금은 멸종된 AsyncTask이다. (사실 자바에선 RxJava로 바꾸기 귀찮아서 쓰는 곳도 있음)
AsyncTask를 처음 접했을때도 엄청 좋다고 생각했는데..

Coroutine은 이보다 더 대단하다!👍
그.. 그치만 코틀린에서만 사용이 가능한 걸..😂
서러워서 코틀린 배운다..

먼저 안드로이드 개발자 공식 홈페이지에 따르면 "코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다." 라고 쓰여 있습니다.

Coroutine의 장점

  • 경량
  • 메모리 누수 감소
  • 기본으로 제공되는 취소 지원
  • Jetpack 통합

대충 위의 장점들을 보면 메모리 관리와 취소가 가능하다는 점이 큰 메리트로 다가옵니다.


이해하기 쉽게 아래 코드에서 살펴보면

@Dao
interface UserProfileDao {
    @Insert
    suspend fun insert(userProfile: UserProfile)
    @Delete
    suspend fun delete(userProfile: UserProfile)
}

데이터 베이스에서 삽입과 삭제를 하는 함수입니다.
코루틴에서는 함수에 suspend 키워드를 사용하여 일시정지가 가능한 함수로 만들어야 합니다.

class UserProfileRepository(private val userProfileDao: UserProfileDao) {

    suspend fun insert(name: String, phone: String, address: String) {
        userProfileDao.insert(UserProfile(name, phone, address))
    }

    suspend fun deleteAll(){
            userProfileDao.deleteAll()
    }
}

이제 Repository에서 파라미터에 사용자의 name, phone, address를 넣는 insert()함수와
사용자 전체의 정보를 삭제하는 deleteAll()함수를 만들어 주었습니다.
이때 ViewModel에서 바로 함수를 만들지 않고 Repository를 통해 만들어 주는 이유는 따로 있는데.. 다음에 따로 알아보죠!🤨

이제 ViewModel에서 Repository의 insert()deleteAll()함수를 구현 해주었는데

class UserProfileViewModel(application: Application) : AndroidViewModel(application) {

    var userName: String? = null
    var userPhone: String? = null
    var userAddress: String? = null
    private val repository: UserProfileRepository 


    init {
    /* db는 싱글톤 방식으로 구현 */
        val db = UserProfileDatabase.getInstance(application.applicationContext)!!
        repository = UserProfileRepository(db.getUserProfileDao()!!)
    }

    fun insert(name: String, phone: String, address: String) {
        CoroutineScope(Dispatchers.IO).launch {
            repository.insert(name, phone, address)
        }
    }

    fun deleteAll(){
        CoroutineScope(Dispatchers.IO).launch {
            repository.deleteAll()
        }
    }

위에서 보면 repository 객체를 만들어주고 insert()deleteAll()을 사용해주죠
이때 CoroutineScope는 이 함수는 코루틴 스코프에서 사용한다고 선언하고 Dispatchers.IO는 이 작업을 IO스레드에서 비동기 처리한다 라고 선언하는 것입니다.
근데 Dispatchers는 또 뭘까요..??

Coroutine Dispatcher 에는 크게 세 가지가 있는데

  • Main: UI와 상호작용하는 작업을 실행하기 위해서만 사용
  • IO: 디스크 또는 네트워크 I/O 작업을 실행하는데 최적화
  • Default: CPU를 많이 사용하는 작업을 기본 스레드 외부에서 실행하도록 최적화

이렇게 코루틴에서는 함수를 처리할 Thread를 정하기 위해 Dispatcher들이 존재합니다.

종합 해보면 먼저
suspand 키워드를 사용하여 일시정지가 가능한 함수로 만들어주고 Dispatcher로 함수가 실행될 Thread를 정해주고 coroutine scope안에서 함수를 실행하면 되겠죠👍


이렇게만 보더라도 자바에서 사용하던 AsyncTask처럼 클래스 만들고.. 오버라이딩 함수 구현하고..
이런 작업을 안해도 돼서 개인적으로 너무 편합니다...👏

다들 코루틴 사용하십쇼!!😊


Reference

Android의 Coroutine

profile
꿈을 현실로 구현할 수 있도록

0개의 댓글