본문 바로가기
Android

[Android] Android Threads

by Saerong2 2020. 10. 2.

 

 

자바로 작성한 프로그램은 JVM(안드로이드의 경우 ART)이 프로그램의 시작점에 해당하는 메인함수를 찾아 실행합니다. 그래서 일반적으로 자바로 프로그램을 작성해야 한다면 main( ) 함수를 구현해야 합니다. 하지만 안드로이드의 경우 메인함수가 안드로이드 프레임워크의 "android.app.ActivityThread" 클래스에 구현되어 있기 때문에 개발자가 직접 메인함수를 작성하지 않고 Manifest 파일에서 액티비티 중 하나를 Launcher로 지정함으로써 해당 액티비티를 어플리케이션의 진입점(Entry Point) 으로 만들 수 있습니다.

 

 

메인 스레드(Main Thread)

안드로이드의 진입점(Entry Point)을 통해서 어플리케이션이 실행되면, 안드로이드 시스템은 처음에 하나의 스레드로 애플리케이션의 Linux 프로세스를 생성합니다. 이 때 실행되는 최초의 스레드를 메인 스레드라고 합니다. 위에서 이야기 한 "android.app.ActivityThread" 클래스의 메인함수에서는 앱이 처음에 동작하기 위해 필요한 여러가지 작업을 수행하는데, 그 중 하나가 바로 메인 스레드를 실행하는 것입니다.

 

메인 스레드 A.K.A UI 스레드

메인 스레드의 가장 중요한 역할 중 하나는 화면을 그려주는 역할입니다. 그래서 메인 스레드를 UI 스레드라고도 부릅니다. 모바일에서 화면은 사용자와 직접적으로 맞닿아 있는 부분이기 때문에 다양한 UI 구성요소를 화면에 제때 정확히 그려주는 것은 중요한 일입니다. 앱의 버튼을 눌렀는데 묘하게 느리게 반응한다면 사용자 경험을 무척이나 해치게 되겠죠.

 

하나의 프로세스에서 실행되는 모든 구성요소는 해당 프로세스의 UI 스레드를 통해서 인스턴스화 되고 각 구성요소에 대한 시스템 콜은 해당 스레드에서 호출됩니다. 즉, 사용자와 상호작용하는 onKeyDown( )이나 onPase( )등의 콜백함수는 항상 프로세스의 UI 스레드에서 실행됩니다.

보다 구체적인 예시로 사용자가 화면의 버튼을 눌렀을 때는 어떠한 일이 발생할까요?

사용자가 화면의 버튼을 누르면 UI 스레드가 터치 이벤트를 해당 버튼 위젯에게 보냅니다. 버튼 위젯은 상태를 Press 상태로 바꾸고 눌린 상태에서 필요한 작업을 수행합니다. 버튼이 눌린 것을 화면에 갱신하기 위해 버튼 위젯은 자기 자신을 다시 그리라는 메세지를 이벤트 큐에 등록합니다. UI 스레드는 이벤트 큐에 등록된 요청을 확인하고 버튼 위젯에게 요청을 전달해 버튼 위젯의 영역을 Press 상태로 다시 그리도록하여 화면을 갱신합니다.

 

ANR (Application Not Responding)

오직 UI 스레드만이 화면을 그릴 수 있기 때문에 UI 스레드가 바쁠 경우 낮은 반응성을 보이며 화면의 갱신이 느려지게 됩니다. 화면은 앱이 사용자와 상호작용을 할 수 있는 직접적인 매개체이며 보통 60 프레임 이상 나와야 사용자 경험을 해치지 않는다고 합니다. 그렇기 때문에 UI 스레드가 오래걸리는 작업을 하게 되면 화면의 갱신이 느려지며 60 프레임 미만으로 떨어질 수 있고 심한 경우 5초 이상 화면을 갱신하지 못하며 ANR을 맞닥뜨리게 됩니다. UI 스레드가 바쁜 상태에 들어가 5초 이상  사용자와 상호작용 할 수 없는 경우를 막기 위해 안드로이드에서는 ANR이라는 다이얼로그를 띄워서 사용자가 어플리케이션을 강제로 종료할 수 있도록 합니다. ANR 상태에 빠져서 홈키에 대한 반응도 처리하지 못하고 전화 수신버튼 조차 누를 수 없다면 난감하겠죠?

따라서 네트워크를 통해서 데이터를 가져온다거나, DB 쿼리를 보낸다던가 하는 등의 시간이 오래 걸리는 작업은 UI 스레드에서 해서는 안되며 새로운 Worker 스레드를 만들어서 처리해야합니다.

 

근데 시대가 어떤 시대인데 단일 스레드를 사용한담...?

안드로이드에서는 Java SDK의 Thread를 사용하며 이를 이용해 당연히 멀티스레드를 사용할 수 있지만 UI의 업데이트에는 단일 스레드를 사용합니다. 그렇다면 왜 안드로이드에서는 UI 스레드만이 화면을 그릴 수 있도록 제한하였을까요?

한 화면의 구성은 여러 위젯으로 이루어져 있고, 순서가 섞이게 되면 의도한대로 화면의 구성이 이루어지지 않기 때문에 위젯들이 그려지는 순서가 중요합니다. 하지만 멀티 스레드는 스레드간의 실행 순서가 보장이 되지 않기 때문에 여러 스레드에서 화면을 그리면 개발자가 의도하지 않은 화면이 그려질 수 있습니다.

예를 들어, 화면 전체에 빨간색 배경 위에 버튼을 그려야하는데 먼저 실행된 스레드에서 버튼을 그리고 다른 스레드에서 그 위에 빨간색 배경을 덮어버리면 버튼이 사라지게 되겠죠. 그래서 하나의 UI 스레드에서만 화면을 그리게 됩니다.

 

워커 스레드(Worker Thread)

화면을 그리는 작업은 UI 스레드에서만 처리할 수 있기 때문에 사용자 경험을 해치지 않기 위해서 UI 스레드는 대기시간이 길거나 실행시간이 긴 작업은 절대 피해야합니다. 그래서 시간이 오래걸리는 작업이 있고 이를 UI 스레드의 실행흐름과 동시에 처리할 수 있다면 Worker 스레드를 만들어서 병렬적으로 처리해야합니다.

만약에, 워커 스레드에서 처리한 작업을 뷰에 반영해야 한다면 어떻게 해야할까요? 화면을 그리는 역할은 무조건 UI 스레드가 해야하기 때문에 처리한 작업을 UI 스레드에 알려주기 위한 스레드 간의 통신이 필요합니다. 스레드간의 통신에 대해서는 다음 글에서 다루도록 하겠습니다.

 

 

 

 

* 도움을 얻은 글

https://recipes4dev.tistory.com/143?category=768056

https://tigerwoods.tistory.com/26

댓글