본문 바로가기

Mobile/Android

[Android] Thread / Handler / Message

매번 미루던 안드로이드 정리,, 한다 이젠,,

 

+ Service 정리도 해야한다.

 

- 안드로이드에서는, Main Thread 를 통해서만 화면 UI 를 변경할 수 있다.

 

-> 따라서, 핸들러를 통해서 Main Thread 에 접근하여 UI 를 수정할 수 있다. 

 

- 안드로이드에서 반복 작업을 하기 위해서는 핸들러 객체를 써야 하는데,

 

  '반복 작업'을 하는 타이머와 같은 것들을 구현하면서 가볍게 넘겨버린 개념이 확실하지 않아서,, 

 

- 스레드와 핸들러의 개념은 꼭 정리해야 하는 것,,

 

스레드(Thread) 란? 

 

  1.  스레드의 정의

 

    1)  동시에 수행 가능한 작업 단위

 

      -  하나의 프로세스 안에서 동시에 수행되어야 하는 작업을 위해 사용

 

    2)  프로세스 내에서 '순차적으로 실행되는 실행 흐름' 의 최소 단위

 

      -  프로그램의 'main() 함수로부터 시작되는 최초 실행 흐름' = '메인 스레드'

 

    3)  안드로이드 앱에서 메인 스레드

 

        -  안드로이드에서 UI를 처리할 때 사용되는 기본 스레드 

 

         Ex)  메시지 큐(Message Queue) 수신을 대기하는 루프 실행

 

        -  사용자 입력과 시스템 이벤트, 화면 그리기 등의 메시지가 수신되면

 

       -> 각 메시지에 매핑된 핸들러의 메서드 실행한다.

 

 

  2.  스레드의 특징

 

    -  같은 프로세스 안에 들어 있으면서 메모리 리소스를 공유(효율적)

 

    -  동시에 리소스에 접근 시 데드락(DeadLock) 발생 가능 -> 이때, 작업을 순서대로 처리하게 도와주는 핸들러로 해결 가능

 

    -  같은 프로세스 안에서 일련의 기능이 순서대로 실행될 때 : 문제 X

 

    -  대기 시간이 길어지는 네트워크 요청 등의 기능 수행 시 화면에 보이는 UI도 멈추는 상태로 존재하는 문제 발생 

 

   ->  서비스 / 멀티 스레드를 통해 해결 가능!

 

 

  3.  스레드 사용 시기

 

    -  구현하고자 하는 기능이, 메인 스레드와 병행적(Concurrently)으로 실행되어야 할 때 별도의 스레드 작성 필요

 

      Ex) 어떤 기능을 - 메인 스레드에 구현

 

           다른 기능을 구현하고자 할 때, 메인 스레드의 동작에 영향을 주는가?

 

      ->  만약, 다른 기능의 실행 시간이 오래 걸리거나, 외부 데이터를 수신하기 위해 대기 상태에 머물러야 하는 경우

 

      ->  메인 스레드의 동작에 영향을 줄 수 있어, 별드의 스레드 작성 필요

 

 

  4.  멀티 스레드

 

    (1)  멀티 스레드(Multi Thread) 개념

 

      -  '하나의 프로세스 내'에서, '둘 이상의 스레드'가 동시에 작업을 수행하는 것

 

     +  멀티 프로세스(Multi Process) : 여러 개의 CPU를 사용해 여러 프로세스를 동시에 수행하는 것

 

      -  멀티 프로세스는, 각 프로세스가 독립적인 메모리를 가지고 별도로 실행

 

         멀티 스레드는, 각 스레드가 자신이 속한 프로세스의 메모리를 공유

 

    (2)  멀티 스레드의 장점

 

      -  각 스레드가 자신이 속한 프로세스의 메모리 공유 -> 시스템의 자원 낭비 감소

 

      -  하나의 스레드가 작업할 때 다른 스레드가 별도의 작업 진행 가능 -> 사용자와의 응답성도 좋아짐

 

    (3)  멀티 스레드의 프로그래밍

 

      -  멀티 스레드 프로그래밍에서 고려해야 할 사항이 싱글 스레드 프로그래밍 보다 많아짐

 

      -> 안드로이드의 메인 스레드가 아닌 스레드에서 뷰(View)에 대한 직접적인 접근 시 오류가 발생한다.

 

      -> 기능은 반드시 메인 UI 스레드에서 실행되어야 함

 

      -> 스레드 간 통신 필요

 

 

스레드와 스레드 통신(Thread Communication)

   

    -  스레드 통신이란, 하나의 스레드에서 -> 다른 스레드로 데이터를 전달하는 것

   

 

  1.  스레드의 성질(독십성)

 

    -  모든 스레드는, 기본적으로 '독립적인 실행 흐름' 을 가짐

 

    -  일단, 스레드가 실행되고 나면 -> 다른 스레드로부터 어떠한 간섭도 받지 않고, 다른 스레드가 어떻게 실행되고 있는지 관심을 두지 않는다.

 

   ->  그러나, 이러한 독립성은 스레드의 본질적 특징일 뿐

 

   ->  스레드의 상태나 결과를 -> 다른 스레드로 전달하도록 의도적으로 구현해야 하는 경우가 대부분

 

 

  2.  스레드 통신 예시

 

    -  USB 메모리에 있는 파일을 내부 스토리지로 복사해야 하는 상황

 

    -  파일 크기 = GB 단위인 경우, 복사 완려까지 많은 시간 소요

 

   ->  파일 복사 기능 = 메인 스레드가 아닌, 별도의 스레드를 통해 처리 => 파일 복사 스레드

 

    -  Main 화면에서 "COPY" 버튼을 통해 새로운 스레드가 실행되도록 만들기

 

    -  이때, 파일 복사가 얼마나 진행되었는지 진행 상태/결과를 화면에 표시하기 위해 progress bar 를 추가

 

스레드 통신

      -  그러나, 위와 같이 메인 화면에 progress bar를 추가하고

 

     ->  파일 복사 스레드에서 진행상황을 progress bar에 나타내기 위해 setProgress()로 접근하게 되면 에러가 발생  

 

 "Only the original thread that created a view hierarchy can touch its views."

 

     ->  에러 원인 : 안드로이드에서, '화면 UI 뷰 접근'은 반드시 '메인 스레드' 에서 실행되어야 함!!  

 

     ->  아래와 같이 파일 복사 스레드의 진행 상태메인 스레드로 "메시지 형태"로 전달

 

     ->  메인 스레드에서는 이 메시지 값을 사용해 현재 진행 상태를 화면에 표시

 

Message/Handler를 통한 스레드 통신

 

 

안드로이드 스레드 통신 - 핸들러(Handler)

 

    -  안드로이드에서 사용 가능한 스레드 통신 방법 중 가장 일반적인 방법 : 핸들러를 통한 메시지 전달

 

 

핸들러(Handler) 의 동작
Thread 구성 요소

   

    -  Thread : 프로세스 내의 세부 작업 단위

 

    -  Handler 

 

       -  Message, Runnable Task를 Message Queue 로 전달

 

       -  Message, Runnable Task를 실행

 

    -  Looper : Message Queue 에서 Handler 로 전달

 

    -  Message Queue : Message, Runnable Task 를 저장하는 Queue

 

 

  1.  메시지(Message, android.os.Message)

 

    (1)  메시지의 개념

 

      1)  스레드 통신에서, 핸들러를 사용해 데이터를 보내기 위해 데이터를 한 곳에 저장하는 역할을 하는 클래스

 

      2)  데이터 종류를 식별하는 식별자,

 

         실질적인 데이터를 저장한 객체,

 

         추가 정보를 전달할 객체

 

      3)  하나의 데이터를 보내기 위해 한 개의 Message 인스턴스 필요

 

      -> 데이터를 담은 Message 객체를 핸들러로 보내면

 

      -> 해당 Message 객체는 핸들러와 연결된 메시지 큐에 쌓임

 

 

  2.  메시지 큐(MessageQueue, android.os.MessageQueue)

 

    (1)  메시지 큐의 개념

 

      1)  Message 객체를 큐(Queue) 형태로 관리하는 자료 구조

 

      2)  FIFO(First In First Out)_ 방식으로 동작

 

      -> 메시지는 큐에 들어온 순서대로 차례대로 저장 (First In)

 

      -> 가장 먼저 들어온 Message 객체부터 순서대로 처리(First Out)

 

    (2)  안드로이드에서 메시지 큐

 

      1)  MessageQueue 클래스에 구현

 

      2)  앱의 Main Thread 에서 기본적으로 사용

 

      3)  개발자가 Message 객체를 직접 참조해 메시지를 전달하거나, 메시지를 가져와서 처리 X

 

     -> 메시지 전달 = 메시지 큐에 연결된 핸들러(Handler)를 통해

 

     -> 메시지 큐로부터 메시지를 꺼내고 처리 = 루퍼(Looper) 가 수행

 

 

  3.  루퍼(Lopper, android.os.Looper)

 

    (1)  루퍼의 역할

 

      1)  MessageQueue = Message 객체 리스트를 관리하는 클래스

 

      ->  큐에 쌓인 메시지 처리를 위한 핸들러 실행 X

 

      ->  메시지 큐로부터 메시지를 꺼내오고 -> 해당 메시지와 연결된 핸들러 호출하는 역할 수행

 

      2)  메시지 처리를 위한 메시지 루프(Message Loop) 실행

 

    (2)  안드로이드에서의 Looper

 

      1)  Main Thread 에는 Looper 객체를 사용해 메시지 루프를 실행하는 코드 구현 O

 

        -  해당 루프 안에서 메시지 큐의 메시지를 꺼내어 처리하도록 만들어짐

 

       -> 메인 스레드에서 메시지 루프와 관련된 코드 추가 작성 필요 X 

 

      2)  메인 스레드로 전달할 Message 객체 구성 -> 스레드의 메시지 큐에 연결된 핸들러를 통해 해당 메시지 보내기

  

 

  4.  핸들러(Handler, android.os.Handler)

 

    (1)  핸들러의 역할

      

      -  스레드의 루퍼(Looper)와 연결된 메시지 큐로 메시지를 보내고, 처리할 수 있도록 함

 

      -  Main Thread의 메시지 처리 흐름에서 - 메시지 전달/처리를 위해 개발자가 접근 가능한 창구 역할 수행

 

    (2)  핸들러 구현

 

      -  스레드와 관련된 핸들러를 얻기 위해 : new 키워드를 통해 Handler 클래스 인스턴스 생성

 

     -> 새로운 Handler 인스턴스는, 자동으로 해당 스레드와 메시지 큐에 연결(bound)

 

     -> 이 시점부터, 핸들러를 통해 메시지 전달 및 처리 가능

 

Handler 사용 예시

 

이 다음은, 핸들러(Handler)를 사용해 스레드 통신을 구현하는 방식, 핸들러가 제공하는 추가 기능에 대해,,

 

 

  참고

    -  https://recipes4dev.tistory.com/166

    -  https://uijin.tistory.com/20