프로그래밍/Android Java

안드로이드 프로그래밍 / 멀티쓰레드 #1

배기니어 2021. 1. 31. 02:11

지난 프로그래밍에서 안드로이드 디바이스가 소켓을 생성하는 과정에서 문제가 발생했다.

지난 글 참고

 

2021/01/29 - [프로그래밍/Android Java] - 안드로이드 소켓 프로그래밍 / 클라이언트 프로그래밍 #1

 

안드로이드 소켓 프로그래밍 / 클라이언트 프로그래밍 #1

지난 시간에 파이썬으로 서버를 프로그래밍 했다 2021/01/28 - [프로그래밍/Python] - 파이썬 소켓 프로그래밍 / 서버 프로그래밍 #1 파이썬 소켓 프로그래밍 / 1. 서버 프로그래밍 #1 첫 번째 프로젝트

bagineer.tistory.com

 

메인 쓰레드에서 소켓 생성을 시도해서 에러가 발생했기 때문에 이번에는 쓰레드에 대해서 다뤄보기로 한다.

간단히 말하자면 쓰레드는 프로그램이 실행되는 논리적인 단위 중 하나이다.

비슷한 예로는 프로세스가 있는데 하나의 프로그램에는 여러 프로세스가 있을 수 있고, 다시 하나의 프로세스에는 여러 쓰레드가 존재할 수 있다.

 

예를 들자면 웹브라우저를 실행했을 때 이것을 하나의 프로세스로 보자면 실시간 검색어가 움직이고, 광고 배너가 변하는 등 하나의 프로세스 내에서 여러가지 동작이 동시다발적으로 일어나는 것을 볼 수 있다. 이것을 쓰레드의 개념으로 생각하면 된다.

 

그럼 여러 개의 간단한 동작을 하는 앱을 프로그래밍 해보자!

화면 구성부터 살펴보면 여섯 개의 텍스트뷰가 있다.

main value, thread value 1, thread value 2를 나타내는 텍스트와 그 값을 표시하는 텍스트 뷰이다.

그리고 그 밑에는 버튼이 하나 있다.

이 버튼을 누를 때마다 main value의 값이 1씩 증가한다.

thread value는 자동으로 매 초마다 값이 증가하는데, thread value 1은 1씩, thread value 2는 2씩 증가한다.

즉, 이 앱에는 세 개의 쓰레드가 동작한다고 생각하면 된다.

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <GridLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintVertical_bias="0.4"
        android:columnCount="2"
        android:rowCount="3">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Main Value : "
            android:textSize="30dp">
        </TextView>

        <TextView
            android:id="@+id/mainValue"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            android:text="0"
            android:textSize="30dp">
        </TextView>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Thread Value 1 : "
            android:textSize="30dp">
        </TextView>

        <TextView
            android:id="@+id/threadValue1"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            android:text="0"
            android:textSize="30dp">
        </TextView>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Thread Value 2 : "
            android:textSize="30dp">
        </TextView>

        <TextView
            android:id="@+id/threadValue2"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            android:text="0"
            android:textSize="30dp">
        </TextView>
    </GridLayout>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintVertical_bias="0.6"
        android:text="+ MainValue"
        android:textSize="20dp"
        android:onClick="mOnClick">
    </Button>

</androidx.constraintlayout.widget.ConstraintLayout>

 

mOnClick은 레이아웃에 정의한 버튼의 동작을 정의한 메소드이다.

버튼을 클릭하면 main value 변수인 mainV를 1 증가시키고 main value의 textview인 mMainText에 이 값을 출력한다.

아마 내 기억엔 setText에 그냥 integer값을 넣으면 값이 갱신되지 않았던가 에러가 발생했던가 하는 문제가 있었던 것 같은데 그래서 처음부터 int를 string으로 변환하여 입력하도록 했다.

 

    public void mOnClick(View v) {
        mainV++;
        mMainText.setText(Integer.toString(mainV));
    }

 

MyThread 클래스는 초(sec)와 텍스트뷰 오브젝트를 매개변수로 하여 생성한다.

쓰레드의 sleep 메소드가 ms단위로 값을 받기때문에 인스턴스 변수에는 ms 단위로 초기화한다.

쓰레드가 시작되면 입력한 초마다 값을 1씩 증가하여 갱신하는 동작을 한다.

 

    class MyThread extends Thread {

        int ms, val = 0;
        TextView text;

        public MyThread(int s, TextView t) {
            ms = s*1000;
            text = t;
        }
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(ms);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                val++;
                text.setText(Integer.toString(val));
            }
        }
    }

 

아래는 전체 java 코드이다.

thread의 daemon을 true로 설정한 것은 쓰레드를 데몬 쓰레드로 설정한 것이다.

 

데몬 쓰레드는 메인 쓰레드의 부가적인 기능을 하는 쓰레드로서, 메인 쓰레드가 종료되면 자동으로 종료된다.

부가적인 기능을 하기 때문에 메인 쓰레드가 종료되면 더 이상 의미가 없기 때문이다.

 

MainActivity.java

package com.example.multithread;

import androidx.appcompat.app.AppCompatActivity;

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

public class MainActivity extends AppCompatActivity {

    int mainV = 0;
    TextView mMainText, mThreadText1, mThreadText2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMainText = (TextView)findViewById(R.id.mainValue);
        mThreadText1 = (TextView)findViewById(R.id.threadValue1);
        mThreadText2 = (TextView)findViewById(R.id.threadValue2);
        MyThread thread1 = new MyThread(1, mThreadText1);
        thread1.setDaemon(true);
        thread1.start();
        MyThread thread2 = new MyThread(2, mThreadText2);
        thread2.setDaemon(true);
        thread2.start();
    }

    public void mOnClick(View v) {
        mainV++;
        mMainText.setText(Integer.toString(mainV));
    }

    class MyThread extends Thread {

        int ms, val = 0;
        TextView text;

        public MyThread(int s, TextView t) {
            ms = s*1000;
            text = t;
        }
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(ms);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                val++;
                text.setText(Integer.toString(val));
            }
        }
    }
}

 

완성~

 

실행 결과

 

이제 이 멀티쓰레드를 이용해 소켓생성을 시도해보자!