본문 바로가기
Android/Concepts

Thread와 Handler 사용법

by JuHy_ 2020. 4. 23.

Thread와 Handler란?

앱을 구현할 때 하나의 기능이 실행되는 중 다른 기능이 동시에 실행되어야 할 때가 있다.

예를 들어 우리가 게임을 하는 동안에 동시에 채팅도 할 수 있게 구현하고 싶다면 Thread를 사용하면 된다.

Thread를 사용하면 현재 실행되고 있는 코드와 별도로 시스템에서 자원을 할당하여 동시에 실행시켜 준다.

 

프로젝트를 생성하면 Main Thread 하나로 작동하며, 여기에 다른 Thread를 추가하여 사용할 수 있다.

 

동시에 여러 기능을 수행한다면 Service를 사용하면 되지 않을까 라고 생각할 수 있다.

여러 차이가 있지만 Service는 UI가 없이 동작하는 반면, Thread는 UI에 접근하여 수정하는 것이 가능하다.

 

이 때 여러 가지 Thread가 동시에 UI에 접근함으로써 Deadlock과 같은 Concurrency Problem이 발생할 수 있다.

이를 방지하기 위해 안드로이드에서는 Handler를 통해 UI에 접근하도록 하고 있다.

 

Handler는 Message Queue를 이용하여 Main Thread에서 순차적으로 코드를 수행할 수 있게 해준다.

다른 Thread에서 Message를 통해 요청을 보내면 Main Thread의 Handler에서 Message Queue에 담는다.

이렇게 Message Queue에 담긴 요청들을 문제가 발생하지 않도록 순차적으로 실행하게 된다.

 

사용법 1

package com.juhy.myapplication;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView tv_value;

    ValueHandler handler = new ValueHandler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv_value = findViewById(R.id.textView);

        Button button_start = findViewById(R.id.button_start);
        button_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BackgroundThread thread = new BackgroundThread();
                thread.start();
            }
        });

    }

    class BackgroundThread extends Thread {
        boolean running = false;
        int value = 0;

        @Override
        public void run() {
            running = true;
            while(running){
                value += 1;

                Bundle bundle = new Bundle();
                bundle.putInt("value", value);

                Message msg = handler.obtainMessage();
                msg.setData(bundle);
                handler.sendMessage(msg);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ValueHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            int value = msg.getData().getInt("value");

            tv_value.setText("Value: " + value);
        }
    }

}

먼저 Thread 클래스를 상속받아 우리가 동작시킬 클래스를 정의해주자.

클래스 내부에는 run() 함수를 통해 쓰레드 시작 시 동작할 코드를 정의해준다.

이 때 보통 boolean 값 하나를 만들어 while()문을 통해 반복되는 코드를 통제하는 방법을 사용한다.

 

이제 반복문 안에 쓰레드에서 실행할 코드를 작성해주자.

일단은 간단하게 value라는 정수 값을 1씩 증가시키고 1초 sleep하도록 만들자.

그리고 이 증가시킨 값을 UI를 통해 보여주려면 Handler를 만들어주어야 한다.

 

따라서 Handler 클래스를 상속받아 클래스를 정의해준다.

handleMessage() 함수를 오버라이드하면 수신된 Message 변수가 파라미터로 넘어온다.

이를 통해 bundle 데이터를 뽑아 TextView에 표시해주자.

(Handler 클래스에서는 UI 접근이 가능하지만, Thread 클래스 내부에서 접근할 경우 에러가 발생한다)

 

Handler 클래스를 만들어주었으니 다시 쓰레드로 돌아와 전달하는 부분을 만들어주자.

Bundle 객체를 생성해 전달할 데이터를 담아준 뒤,

Message 객체를 생성해 bundle 객체를 담아 Handler의 sendMessage() 함수를 통해 전달하면 된다.

 

사용법 2

이번에는 클래스를 정의하지 않고 간단하게 사용할 수 있는 방법으로 구현해보자.

 

package com.juhy.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView tv_value;

    Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv_value = findViewById(R.id.textView);

        Button button_start = findViewById(R.id.button_start);
        button_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread(new Runnable() {
                    boolean running = false;
                    int value = 0;
                    @Override
                    public void run() {
                        running = true;
                        while(running){
                            value += 1;

                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    tv_value.setText("Value: " + value);
                                }
                            });

                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();

            }
        });

    }

}

먼저 버튼 클릭 시 미리 정의한 Thread 클래스가 아닌 Thread 객체를 만들어준다.

이 때 Runnable 객체를 넘겨주는데, 이 객체 안에 run() 함수를 override해 thread에서 작동할 코드를 넣어주면 된다.

 

Handler 또한 미리 정의한 클래스가 아닌 Handler 객체를 만들어주면 된다.

그 다음 Handler의 post() 함수를 호출하는데 이 때 Thread와 마찬가지로 Runnable 객체를 넘겨주면 된다.

이번에는 run() 함수에 handler가 수행할 코드인 UI에 접근하는 부분을 담아주자.

 

실행 후 버튼을 눌러보면 정상적으로 1초마다 값이 1씩 증가하는 것을 볼 수 있다.

 

 

Reference

[부스트코스]안드로이드 프로그래밍

https://www.edwith.org/boostcourse-android

'Android > Concepts' 카테고리의 다른 글

Socket을 이용한 서버와 클라이언트 통신  (3) 2020.04.23
AsyncTask 사용법  (0) 2020.04.23
Navigation Drawer Activity 살펴보기  (3) 2020.04.17
ViewPager에 TitleStrip/TabStrip 추가하기  (0) 2020.04.14
ViewPager 사용법  (1) 2020.04.14