ABOUT ME

작은 디테일에 집착하는 개발자

Today
-
Yesterday
-
Total
-
  • [Android/Kotlin] "Can't create handler inside thread that has not called Looper.prepare()" 예외
    IT Study/Android 2024. 4. 4. 22:02
    728x90

    안녕하세요. 개발하며 만나게 된 예외에 대해 다뤄보도록 하겠습니다. 🥺 (너무 오랜만에 블로그 글로 돌아왔습니다...!!!!)

     

    오류 멈춰~ STOP~

     

    1. "Can't create handler inside thread that has not called Looper.prepare()"  오류의 의미

    안드로이드 앱에서 백그라운드 스레드에서 핸들러(Handler)를 생성하려 할 때 발생합니다. 이를 이해하기 위해서는 먼저 안드로이드의 Looper와 Handler 시스템에 대해 알아야 합니다.

    그럼 Handler는 메인 스레드에서만 생성해야하나요?

    핸들러는 *기본적으로 생성된 스레드의 Looper에 연결됩니다.
    메인 스레드에서 생성된 핸들러는 자동으로 메인 스레드의 Looper와 연결되기 때문에, 추가 작업 없이 UI 업데이트를 수행할 수 있습니다. 그러나 백그라운드 스레드에서 핸들러를 사용하고 싶다면, 그 스레드에 Looper를 명시적으로 준비해야 합니다.

    *기본적으로 생성된 스레드라는게 뭐죠?

    핸들러 객체가 생성된 해당 스레드를 의미합니다. 메인 스레드일수도, 백그라운드 스레드일 수도 있겠죠.

     

    2. Looper

    Looper는 스레드의 메시지 큐를 관리하는 클래스입니다. 안드로이드의 메인 스레드(또는 UI 스레드)는 기본적으로 자체 Looper를 가지고 있어, 이벤트와 메시지를 처리할 수 있습니다.
    하지만, 백그라운드 스레드는 기본적으로 Looper를 가지고 있지 않습니다. 따라서, 이 스레드에서 메시지 루프를 실행하려면 Looper.prepare()를 호출해야 합니다.

    루퍼가 뭔데요? 반복되는거에요? 루퍼의 역할 그리고 일반적으로 수행하는 것이 무엇인가요? 어떤 경우 사용되나요? 루퍼 사용하면 무한 반복되는거 아니에요?

    루퍼는 특정 스레드의 메시지 큐를 관리하는 것입니다.
    메시지 큐를 관리한다는 것은 반복적으로 처리할 메시지나 작업이 있다면, 루퍼가 순차적으로 처리하도록 도와준다는 의미입니다.  루퍼는 이벤트나 비동기 작업에 사용되고, 주로 메인 스레드에서 사용됩니다.
    루퍼가 있는 스레드는 메시지 루프를 통해 계속 작업을 처리할 수 있지만, 이것이 항상 무한 반복을 의미하지는 않습니다.

    *메시지 큐를 관리한다는게 어떤 의미죠?

    루퍼가 실행될 작업들을 순서대로 저장하고, 필요할 때마다 하나씩 꺼내 처리하는 역할을 한다는 걸 의미합니다.
    각 스레드는 자신만의 메시지 큐를 가질 수 있고, 이 큐는 작업을 저장하는 대기열 역할을 수행합니다.

     

    3. Handler

    Handler는 스레드 간에 작업을 보내고 처리하는 역할을 합니다. Handler는 Looper와 연결되어 있어야 하며, Looper의 메시지 큐를 통해 작업을 처리합니다.
    메인 스레드에서 생성된 Handler는 메인 스레드의 메시지 큐와 연결되지만, 다른 스레드에서 Handler를 생성하려면 그 스레드에 Looper가 필요합니다.

    핸들러는 (1) 메시지나 코드를 메시지 큐에 넣으면, 루퍼가 (2) 큐를 통해 메시지를 처리하도록 한다?

    네. 핸들러는 실행 가능한 코드(Runnable)을 메시지 큐에 넣는 역할을 합니다.
    그럼 루퍼는 해당 스레드의 메시지 큐를 지속적으로 순회하며 대기 중인 코드를 차례대로 처리합니다. loop() 메서드를 통해 메시지 루프가 시작되고, 큐에 있는 각 메시지나 코드는 핸들러에 의해 처리됩니다.

     

    4. 왜 오류가 발생했을까?

    메인 스레드에서는 Handler를 문제없이 생성할 수 있습니다. 그 이유는 메인 스레드에 이미 Looper가 준비되어 있기 때문입니다. 하지만 백그라운드 스레드에서 Handler를 생성하려면, 먼저 해당 스레드에 Looper를 준비해야 합니다. 이것이 Looper.prepare() 호출이 필요한 이유입니다.

    루퍼가 있어야만, 핸들러를 생성할 수 있다는건가요?

    네. 모든 핸들러는 루퍼와 연결되어 있어야 합니다.

    (추가 질문) 그럼 핸들러보다 루퍼가 더 큰 개념인가요?

    루퍼는 특정 스레드의 메시지 큐 관리자 역할을 합니다. 한 스레드에는 하나의 루퍼만 존재할 수 있죠. 
    핸들러는 루퍼에 의해 관리되는 메시지 큐에 접근해서 작업을 큐에 넣거나 큐에서 처리합니다. 한 스레드에는 여러 핸들러가 존재할 수 있으며, 같은 루퍼를 공유합니다.

     

    5. Can't create handler inside thread that has not called Looper.prepare()

    이 오류 메시지는 Handler를 메인 스레드가 아닌 다른 스레드에서 생성하려 할 때 발생합니다.
    Looper.prepare()는 현재 스레드에 Looper를 초기화하는 메서드입니다. 백그라운드 스레드에서 Handler를 생성하기 전에 Looper.prepare()를 호출해야 합니다.
    Looper.loop()는 Looper가 메시지 큐를 순회하고 메시지를 처리하도록 하는 메서드입니다. Looper.prepare() 다음에 호출되어야 합니다.

    루퍼를 사용하는 방법이 뭔데요? (1) prepare()로 루퍼를 초기화하고, (2) loop()로 큐를 순회하고 메시지를 처리하는건가요?

    네. 루퍼를 사용하기 위해서는 먼저 prepare()를 호출해 현재 스레드에 루퍼를 설정하고,
    이후 loop()를 호출해 메시지 큐를 시작하고, 스레드가 종료될 때까지 계속 큐의 메시지를 처리합니다.

    (추가 질문) 큐의 메시지가 계속 쌓일 때 무한 반복이 될 수 있는거네요?

    만약 메시지 큐에 계속해서 새로운 메시지나 작업이 추가된다면, 루퍼는 계속 그 메시지들을 처리하게 됩니다.
    이는 루퍼가 작업을 무한히 처리하는 '무한 루프' 상태에 있는 것처럼 보일 수 있지만, 루퍼는 설계된 대로 정상 동작하는 것일 뿐.
    문제는 메시지 큐가 과도하게 채워지거나, 처리할 수 없을 정도로 많은 작업이 계속해서 큐에 들어오는 경우입니다.

     

    6. 오류 해결 방법

    1. 메인 스레드에서 Handler 사용하거나
    2. 백그라운드 스레드에서 Looper 준비하여 사용할 수 있습니다.

    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler();
            // Handler를 사용하는 코드...
            Looper.loop();
        }
    }).start();
Designed by Tistory.