본문 바로가기
코딩과 알고리즘

크레이의 앱개발 도전기 #7. 쓰레드편

※ 이 게시글은 크레이의 IT개발 관련 성장기를 다루고 있습니다. 관련지식이 약간 있어야 이해되실 수 있습니다. 가벼운 마음으로 읽어보시면서 흥미가 생기고 의욕이 생긴다면? 개발자의 자질이 있으신 겁니다 :)

오늘은 홍드님의 스레드 강좌를 응용하여 '초시계'를 만들어보았습니다.

https://edu.goorm.io/learn/lecture/15564/현직개발자가-알려주는-안드로이드-앱-개발/lesson/727137/15-thread-and-handler-사용법 

오늘 강좌 내용은 비교적 단순한 내용이라 생각되었는데요.
크레이가 이미 익히 알고 있는 C# 언어의 쓰레드와 같은 개념이어서 그런지도 모르겠습니다.

레드실행중인 하나의 프로그램을 의미합니다.
모든 프로그램은 기본적으로 하나의 레드(Thread)를 가지는데요.
어떤 프로그램은 2개 이상의 레드를 동시에 실행할 수도 있습니다.
이를 멀티 레드(Multi Thread)라고 하지요.

앱에서도 그런 개념으로 레드를 사용할 수 있다니 정말 흥미진진하다는 생각이 들었습니다.
기본소스는 홍드님의 강좌대로 구현하였고, 몇가지 내용을 수정해보았습니다.


수정한 내용은

학창 시절에 멋진 시계를 갖고 있는 친구가 시계 내장 기능인 밀리초까지 측정 가능한 초시계를 이용한 놀이를 하자고 하더군요. 어떤 놀이냐면 초시계를 작동했다가 특정 시간,
예를 들면 1초에 가깝게 중지하는 사람이 이기고, 지면 꿀밤을 맞는 놀이였습니다.
재미있게 놀았던 기억이 나서 비스무리한걸 만들어 보았습니다.

우선, 버튼은 하나만 사용하도록 바꾸었습니다.
그래서 기본 동작이 한번 누르면 쓰레드를 시작하면서 초시계가 작동하는데,
이 때 버튼의 이름이 '중지'로 바뀝니다.

그리고 다시 중지 버튼을 누르면 초시계가 멈추는데요.
버튼 이름은 다시 '시작'으로 바뀝니다.
시작 버튼을 누르면 다시 새롭게 0초부터 초시계가 시작하기 때문이지요.

이 것이 전부입니다. 딱히 비교하는 기능같은건 없습니다. 아직은 기술을 익히는 입장이니까요 :)


소스의 변경 부분

기본적인 내용은 홍드님의 강좌 내용을 참조하시고 크레이가 변경한 부분만 소개하도록 하겠습니다.

activity_main.xml 에서 시작/종료 버튼을 1개만 사용하도록 수정하고,
초시계를 표현할 텍스트 뷰를 추가하였습니다.
텍스트는 잘 보이도록 큼지막하게 적용하였지요.


액티비티쪽 소스는 짧으니까 전체 소스를 바로 적어놓습니다.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
   
tools:context=".MainActivity"
   
android:orientation="vertical">

    <
Button
       
android:id="@+id/btn_toggle"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content"
       
android:layout_weight="1"
       
android:text="시작"
       
/>

    <
TextView
       
android:id="@+id/txtSecond"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content"
       
android:textSize="90sp"
       
android:text="0"
       
android:textAlignment="center"
       
/>
</
LinearLayout>


MainActivity.java 코드에서는 초를 저장할 변수 Second 를 하나 추가했는데요.
코드를 짜놓고 보니 Second 의 첫글자를 소문자로 정의했어야 했는데 깜박했다는 생각이 들었습니다.

public class MainActivity extends AppCompatActivity {

 
// 버튼과 텍스트뷰 컴포넌트
 
Button btn_toggle;
 
TextView txtSecond;

 
// 스레드
 
Thread thread;
 
// 스레드 시작 여부
 
Boolean isThread = false;
 
// 초를 저장할 변수
 
Double Second;


버튼을 하나로 합쳤기 때문에 isThread 변수로 현재 쓰레드가 진행중인지 아닌지 상태를 파악하도록 했는데요.
만약 쓰레드가 작동중이라면 밑줄 친 코드가 작동해서 isThread 상태변수를 false 로 바꿔주고 버튼 이름도 '시작'으로 바꾸고 바로 끝내버립니다. 그 아래 코드를 실행하지 않는 것이지요. 이 것으로 초시계가 딱 멈추는 기능이 구현됩니다.

// 시작/ 종료 토글 버튼
btn_toggle.setOnClickListener(new View.OnClickListener() {
 
@Override
 
public void onClick(View view) {
 
 
// 스레드가 작동중일 때
 
if(isThread==true) {
  
// 스레드를 빠져나오도록 상태변수 변경하고,
  
isThread=false;
  
// 버튼 글자 바꾸고
  
btn_toggle.setText("시작");
  
// 끝낸다
  
return;
  }
    :

하지만 쓰레드가 작동중이지 않다면 바로 다음 코드들을 실행합니다.
그래서 초시계가 작동을 시작하지요. 초시계는 100분의 1초 (0.01초) 단위로 빠르게 작동합니다.

   // 스레드가 작동중이지 않으면 이쪽으로 넘어와
  
// 상태 변수를 바꿔주고
  
isThread = true;
  
// 버튼 글자 바꾸고
  
btn_toggle.setText("중지");
  
// 0초부터 시작
  
Second=0.0;
  
// 스레드 작동할 내용을 정의한 다음
  
thread = new Thread() {
   
public void run() {
    
// 상태변수 값이 false 로 바뀔 때까지 무한 반복
    
while (isThread) {
     
// 스레드 핸들러 실행
     
handler.sendEmptyMessage(0);
     
try {
       sleep(
10);
      }
catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   };
  
// 작동 시작!
  
thread.start();
  }
 });
}

핸들러의 경우 토스트 메시지 대신 초시계의 진행초를 보여주도록 구현하였습니다.
크레이가 다루는 C# 언어에서는 쓰레드 안에서 컴포넌트 접근을 못하기 때문에 Invoke 라는걸 사용하는데
아마도 안드로이드에서도 쓰레드 안에서 직접 컴포넌트 접근을 하지 못하기 때문에
비슷한 개념을 사용한 건 아닌가 추정됩니다. 뭐 정주행하고 이것 저것 읽어보면 알게 되겠지요.

// 핸들러 정의
private Handler handler = new Handler(){

 
// Ctrl + O 로 함수를 쉽게 만들수 있다
 
@Override
 
public void handleMessage(@NonNull Message msg) {
 
txtSecond.setText(String.format("%.2f", Second));
 
Second+=0.01;
 }
};

그래서 MainActivity.java 전체코드는 아래와 같습니다. 

MainActivity.java

package com.cray.excercise;

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

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

public class MainActivity extends AppCompatActivity {

 
// 버튼과 텍스트뷰 컴포넌트
 
Button btn_toggle;
 
TextView txtSecond;

 
// 스레드
 
Thread thread;
 
// 스레드 시작 여부
 
Boolean isThread = false;
 
// 초를 저장할 변수
 
Double Second;

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

 
// 시작/종료 토글 버튼
 
btn_toggle=(Button)findViewById(R.id.btn_toggle);

 
txtSecond=(TextView)findViewById(R.id.txtSecond);

 
// 시작/ 종료 토글 버튼
 
btn_toggle.setOnClickListener(new View.OnClickListener() {
  
@Override
  
public void onClick(View view) {
   
   
// 스레드가 작동중일 때
   
if(isThread==true) {
    
// 스레드를 빠져나오도록 상태변수 변경하고,
    
isThread=false;
    
// 버튼 글자 바꾸고
    
btn_toggle.setText("시작");
    
// 끝낸다
    
return;
    }

   
// 스레드가 작동중이지 않으면 이쪽으로 넘어와
   
// 상태 변수를 바꿔주고
   
isThread = true;
   
// 버튼 글자 바꾸고
   
btn_toggle.setText("중지");
   
// 0초부터 시작
   
Second=0.0;
   
// 스레드 작동할 내용을 정의한 다음
   
thread = new Thread() {
    
public void run() {
     
// 상태변수 값이 false 로 바뀔 때까지 무한 반복
     
while (isThread) {
      
// 스레드 핸들러 실행
      
handler.sendEmptyMessage(0);
      
try {
        sleep(
10);
       }
catch (InterruptedException e) {
        e.printStackTrace();
       }
      }
     }
    };
   
// 작동 시작!
   
thread.start();
   }
  });
 }

 
// 핸들러 정의
 
private Handler handler = new Handler(){

 
// Ctrl + O 로 함수를 쉽게 만들수 있다
 
@Override
 
public void handleMessage(@NonNull Message msg) {
  
txtSecond.setText(String.format("%.2f", Second));
  
Second+=0.01;
  }
 };
}

소스 구성이 가능하신 분이라면 1초의 기적에 한번 도전해 보시렵니까? :)
크레이는 1.03초가 최고 기록더라구요 ㅎㅎ..


아무쪼록 필요하신 분에게 도움이 되셨기를 기대하며,
오늘은 여기서 이만 휴식을 취하러 가지만
크레이의 앱개발 도전은 계속됩니다~

방문해주신 모든 분들께 감사드립니다:)