안드로이드 개발자라면 들어봤을 법한 Thread와 ANR에 관해 이야기해보려고 한다.
위의 주제에 앞서 먼저 프로세스에 대해 설명하면, 프로세스는 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램을 말한다. 기본적으로 같은 애플리케이션의 모든 구성 요소는 같은 프로세스와 스레드에서 실행되고, 대부분 애플리케이션은 이를 바꿔서는 안 된다. 그럼 스레드란 무엇일까?
1. Thread(스레드)
- 스레드란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미한다. 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다. 또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.
- 애플리케이션이 시작되면 시스템이 애플리케이션에 대한 실행의 스레드를 생성하며, 이를 메인 스레드라한다. 메인 스레드는 drawble 이벤트를 포함하여 적절한 사용자 인터페이스 위젯에 이벤트를 발송하는 역할을 맡기 때문에 중요하다.
대부분의 메인 스레드는 Android UI 도구 키트의 구성요소와 개발자의 애플리케이션이 상호작용하는 스레드이다. 그래서 이를 보통 UI 스레드라 부르는데 특수한 상황에서 앱의 기본 스레드가 UI 스레드가 아닐 수도 있다.
- 시스템은 구성 요소의 각 인스턴스에 대해 별도의 스레드를 생성하지 않는다. 같은 프로세스에서 실행되는 모든 구성요소는 UI 스레드에서 인스턴스화 되고 각 구성요소에 대한 시스템 호출은 해당 스레드에서 발송된다.
따라서 시스템 콜백에 응답하는 메서드는 항상 프로세스의 UI 스레드에서 실행된다. 예를 들면, 사용자가 화면 위젯을 터치할 때 앱 UI 스레드가 위젯에 터치 이벤트를 발송하고, 위젯은 눌림 상태를 설정한 뒤 이벤트 큐에 무효화 요청을 한다.
- 만약 UI 스레드에서 리소스를 많이 소모하는 작업을 수행하는 경우 네트워크 액세스나 데이터베이스 쿼리 등의 긴 작업을 수행할 때 전체 UI가 차단된다. 스레드가 차단되면 모든 이벤트가 발송되지 않고, 사용자에게는 애플리케이션 중단된 것처럼 보인다.
더욱 심각한 상황에는 ANR이 발생하는 것이다.
그럼 ANR은 무엇일까?
2. ANR(Application Not Response)
- Android 앱 내의 UI 스레드가 너무 오랫동안 차단되면 ANR(애플리케이션 응답 없음) 오류가 발생한다.
- 발생 조건
- 활동이 포그라운드에 있는 동안 앱이 입력 이벤트 또는 브로드캐스트 리시버에 5초 이내에 응답하지 않았을 때
- 보통 스레드의 충돌에 의해 발생한다.
ANR을 예방하기 위해서 onCreate() 또는 onResume() 같은 안드로이드의 핵심 생명주기에 속해있는 메서드에서는 가능한 적은 일을 수행하도록 코드를 작성한다.
또한 시간 소모가 많은 작업은 스레드를 통해 처리하고, 사용자에게 프로그래스바 등을 이용해 작업의 진행 과정을 안내해 기다리도록 한다.
모바일 앱의 특성상 화면에 움직임이 없을 경우 사용자들의 터치에 의해 ANR이 발생할 수 있기 때문에 앱이 강제종료 되는 것을 막기 위해 로딩이 오래 걸리는 상황에서는 프로그래스 바를 보여주자.
이러한 상황이 발생하지 않게 하기위해서 별도의 스레드에서 작업을 수행해야 한다. 그러나 메인 스레드를 제외한 다른 스레드에서는 UI 업데이트를 할 수 없기 때문에 runOnUiThread, post, postDelayed 메소드를 사용해야 한다.
작업이 복잡해질수록 코드가 복잡해지고 유지관리가 어려워지기 때문에 작업자 스레드에서 Handler를 사용해 UI 스레드에서 전달받은 메시지를 처리하거나 AsyncTask를 사용하는 것이 좋다.
3. Handler( Handler)
- 메시지 큐를 통해 순차적으로 메인 스레드에서 처리한 메시지를 전달하는 역할을 담당
작업 스레드에서 UI작업이 필요한 순간에 핸들러를 통해 메인 스레드로 작업을 요청한 뒤 메시지를 수신한 메인 스레드에서 해당 작업을 처리해야한다.
핸들러는 핸들러 갹채룰 만든 스레드와 해당 스레드의 Message Queue에 바인딩된다. 메시지 큐는 핸들러가 전달하는 메시지를 보관하는 FIFO방식의 큐이다.
다른 스레드에게 메시지를 전달하려면 수신 대상 스레드에서 생성한 핸들러의 post나 sendMessage 등의 함수를 사용해야 한다. Message Queue에 저장된 message나 runnable은 Looper가 차례로 꺼내서 핸들러로 전달한다.
- 핸들러를 통한 작업 처리 예
작업 스레드에서 UI 처리를 위해 메인스레드에서 생성된 핸들러의 sendMessage()를 통해 메시지를 전달하면 해당 메시지는 메인 스레드의 Message Queue에 저장된다. 루퍼는 차례대로 메시지를 꺼낸다. 작업 스레드가 전달했던 UI 처리를 위한 메시지가 꺼내지면
handleMessage()로 전달된다. handleMessage()는 실제 UI 작업을 수행하는데, 이는 메인 스레드에서 생성된 핸들러에 의해 호출된 것이기 때문에 해당 UI 작업들은 메인 스레드에서 동작하는 것이다. 그러므로 문제없이 UI 작업이 가능하다.
- Handler 주요 함수
Handler.sendMessage(Message msg) : Message 객체를 message queue에 전달하는 함수
Handler.sendEmptyMessage(int what) : Message의 what 필드를 전달하는 함수
Handler.post(new Runnable()) : Runnable 객체를 메시지 큐에 전달하는 함수
스레드와 핸들러 및 ANR에 관해 정리해보았다. 실제로 안드로이드 개발을 하다 보면 ANR 오류를 겪는 일이 종종 있을 텐데, 위의 내용을 숙지하여 오류 발생 빈도를 줄이려고 한다.
참고
프로세스 및 스레드 개요 | App quality | Android Developers
프로세스 및 스레드 개요 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 애플리케이션 구성 요소가 시작되고 애플리케이션에 실행 중인 다른 구성 요소가
developer.android.com