반응형

여러개의 스레드가 TextView에 동시에 접근 하려 하는 멀티 스레드같은 방식에서 공통 메모리 리소스에 접근하지 않도록 개발자가 직접 관리를 해주어야 한다.

 

이때 메인 스레드에서 UI의 수정을 할 수 있고, 핸들러에서 UI의 수정을 담당 할 수 있는데

 

우선 메인 스레드에서 관리 하는 코드를 보고자 한다.

 

Thread

<< activity_main.xml >>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="진행 값"
        android:id="@+id/text1"
        android:layout_marginBottom="30dp"
        android:textSize="30dp"/>

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="시작"
        android:layout_marginBottom="30dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn2"
        android:text="진행 값 확인"/>
</LinearLayout>

 

위와 같은 xml 파일을 만들어 UI를 구성해준다.

 

 

<< MainActivity.java >>

package com.example.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    TextView textView;

    int value = 0;

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

        textView = findViewById(R.id.text1);

        // 스레드를 만들어 실행하는 리스너
        findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BackgroundThread thread = new BackgroundThread();
                thread.start();
            }
        });

        findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 메인 스레드 내에서 변경을 하기에 가능하다.
                textView.setText("현재 값 : " + value);
            }
        });
    }

    class BackgroundThread extends Thread {
        boolean running = false;

        @Override
        public void run() {
            running = true;
            while(running){
                value += 1;
                // 메인 스레드가 아니기에 여기서 UI 수정은 불가능하다.
                // textView.setText("현재 값 : " + value);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

BackgroundThread 이너 클래스를 생성하여 Thread를 상속하게 하여 이 객체를 start하면 스레드가 동작되도록 해준다.

 

이때 public void run(){}에서 value가 1씩 1초마다 증가하도록 하는 스레드를 만든다.

 

이 스레드가 동작하는 동안 '진행 값 확인'이라는 버튼을 누르면 Main Thread에서 UI 수정을 담당하게 된다.

 

이때 BackgroundThread에서 textView.setText를 하게 되면 메인 스레드가 아니기에 Error 나타나게 된다. 

 

 

 

 

Handler

안드로이드에서는 핸들러라는 기능을 제공해주고 스레드에서 핸들러로 작업을 요청하여 핸들러에서 UI를 수정할 수 있게 해준다.

 

핸들러

- 메시지 큐를 이용해 메인 스레드에서 처리할 메시지를 전달하는 역할을 담당

- 특정 메시지가 미래의 어떤 시점에 실행되도록 스케줄링 할 수 있음

 

이때 UI를 수정하는 핸들러는 Main Thread라고 한다.

 

 

<< activity_main.xml >>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="진행 값"
        android:id="@+id/text1"
        android:layout_marginBottom="30dp"
        android:textSize="30dp"/>

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="시작"/>
</LinearLayout>

 

스레드에서 만든 xml에서 '진행 값 확인' 버튼만 사라진 모습이다.

 

 

 

<< MainActivity.java >>

package com.example.myapplication;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    TextView textView;

    MyHandler myHandler = new MyHandler();

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

        textView = findViewById(R.id.text1);

        // 스레드를 만들어 실행하는 리스너
        findViewById(R.id.btn1).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 message = myHandler.obtainMessage();
                message.setData(bundle); // set으로 넣고 get으로 뺀다.

                myHandler.sendMessage(message);

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

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

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

            // 핸들러 내에서 변경을 하기에 가능하다.
            textView.setText("현재 값 : " + value);
        }
    }
}

 

BackgroundThread에서는 Bundle에 현재 1초에 1씩 증가하는 value를 담아주고, MyHandler에게 bundle를 던져주는 모습이다.

 

MyHandler 클래스는 Handler를 상속받아 만들어내고, 위의 스레드에서 setData해준 bundle을 getData()로 받아내어

해당 Bundle속에 있던 int 값인 value를 getInt("value")로 받아낸다.

이런 식으로 스레드를 이용하거나 핸들러를 이용하면 UI를 관리 할 수 있게 된다.

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형