[안드로이드 스튜디오] Ch17. 쓰레드를 이용한 진행바 만들기 (Building A PrgressBar by Using Threads)
2019년 9월07일 (토)
*질적으로 많이 부족한 글입니다. 지적 및 조언 환영합니다.
*이 글에선 "안드로이드 스튜디오"를 줄여서 "안스" 라고 표현합니다. 참고 바랍니다.

우선, 본인은 학원에서 'JAVA' 기초 수업을 선수강했다. 안드로이드 스튜디오를 배우기 위해선 피할 수 없는 언어이기 때문이다.
잡담: 동일한 날짜의 두번째 포스팅이다. 사실 이번 글과 저번글은 모두 같은날 학원에서 배운내용이다. 하지만 굳이 이렇게 두개의 포스팅으로 나눈이유는 이번에 배울 thread 코드가 너무 길기때문이다 (...) 또한 이번 포스팅 이후로도 대부분 하나의 프로젝트 내에서 상당히 긴 코드들이 나오기에 한 포스팅당 하나의 프로젝트만 다룰 예정이다.

이번글은 *Thread 라는 녀석과 마주하게 된다. 개념자체는 Handler와 동일하다.
또한 다이얼로그와 동시에 진행바 생성을 위한 생성자로 *ProgressDialog라는 녀석도 잊지말고 숙지하자!
thread 에 대해서 잠깐 복습하고 넘어간다.
Thread: 여러개의 코드를 동시에 실행시킬수 있게 만들수 있는 것...
그러하다. 보통 이런 thread가 두개이상 모여있게되면 '멀티쓰레드' 라고 부른다고 한다.
(**...잉? 근데 handler가 있는데 왜 굳이 thread를 이용하는가? 감히 추측을 해보자면 handler라는 클래스는 내부클래스로써는 생성이 가능하지만 외부의 독립적인 java클래스 생성이 불가능해서 그런게 아닐까? 이에 관한 정답을 아시는분은 댓글로 부탁드립니다!ㅎㅎ)
<차례>
1. MyThreadActivity - 두개의 다른 진행바 만들기
1. MyThreadActivity - 두개의 다른 진행바 만들기
1.1 결과



설명: 초기화면을 위해 버튼 두개를 만들어주고 thread를 위한 독립적인 java클래스를 하나 생성해준다. 그 안에서 기본적인 기능을 설정해주고 MainActivity에서 thread 클래스를 가져와 activity_main과 '퓨전(?)' 시킨다.

1.2 코드
1.21 activiti_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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/wheel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pregress Wheel"
android:textSize="20dp"/>
<Button
android:id="@+id/bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pregress Bar"
android:textSize="20dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
설명: 딱히 짚을점은 없다.
1.22 ThreadEx.java
package com.example.mythreadactivity;
//우리가 사용할 Thread클래스 정의....
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
public class ThreadEx extends Thread{
private Handler handler; //쓰레드의 값등을 제어하는 클래스...
private boolean check = true;
//생성자
public ThreadEx(Handler handler){
this.handler = handler;
}
public void setCheck(boolean check){
this.check = check;
//run메소드에서 반복할때 Check값이
//true일때만 반복하도록 하고 false일때는 반복문을
//탈출하여 Thread를 소멸되도록 하기 위함...
}
//스레드가 해야할일들을 정의한 메소드..
@Override
public void run() {
int value = 0;
while(check){
try{
Thread.sleep(100); // 내가 지정한 시간 만큼 멈추어 주는 메소드...
}catch (InterruptedException e){
e.printStackTrace();
}
Message msg = handler.obtainMessage();
//Message객체는 정보를 저장하는 객체...
//Bundle객체로만 저장할수 있다...
Bundle bundle = new Bundle();
bundle.putInt("value",value);
msg.setData(bundle);
//Handler에게 메세지정보를 전달하도록 한다...
//핸들러 호출
handler.sendMessage(msg);
value++;
}
super.run();
}
}
설명:
<정리> 각각의 바는 다른 종류의 바다. 하지만 그안에들어가는 '속성'은 동일하다. 그러니까 같은속성의 다르게생긴 '바' 라면
당연히 thread를 이용해서 속성을 만들어주고 이를 각각의 바에 대입시켜주면 되는것이다. inflate라는 개념보다는 interface의 개념이 더 가깝지 싶다.
<세부내용>
(1) ThreadEx라는 이름의 java 클래스 생성 -> Thread 상속 -> handler 호출 (thread의 값 처리는 handler가 한다.)
**잉? 이렇게 되면 결국 Thread는 바짓사장이고 모든일처리는 handler가 다 한다는 이야기가 되는건가?
**그러니까.. handler는 독립적인 외부클래스가 되는게 불가능하니까 Thread의 힘을 빌려 밖으로 나온것뿐이고, 그냥 밖으로 나와서도 하는 일은 동일하다는 의미인걸까? 아시느분은 댓글부탁드립니다. 하하
(2) setCheck 메소드를 통해 이후 thread의 'run'이라는 반복문내에서 이용될 논리형 변수를 생성해준다.
(3) run 메소드를 호출한다.
(4) 진행상태를 숫자로 보여주기 위해선 당연히 int값의 정수가 필요하다. value=0 을 통해 디폴트값을 설정해주자.
(5) while문을 통해 run (진행중) 일때의 기능을 설정해주자.
(6) Thread.sleep(number) 라는 메소드는 특정 클래스의 속도를 ~로 줄이겠다는 기능이다. 그런데 이 기능은 독립적으로 사용이 불가능하다. 무슨말이냐면, 예외처리가 필수라는것이다. 실제로 독립적으로 저 코드 작성시 오류가 나오고 예외처리를 해달라는 알림이 나온다.
따라서 try~catch를 잊지말자!
(7) Message 라는 데이터 저장 기능이 매우 중요하다. 약간 복잡한데 이 과정만 기억하라 [Message > Bundle > 데이터]
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt("value",value);
msg.setData(bundle);
무슨말이냐면, Message가 Bundle형태의 데이터만 저장이 가능하기때문에 데이터를 Bundle안에 넣어두고, 다시 그 Bundle을 Message안에 넣어달라는거다. (...이게 왠 뻘짓이람)

(8) 끝으로 화룡정점인 value++을 넣어 "0부터 시작해서1씩 오르게하겠다"는 명령을 내리자.
1.23 MainActivity.java
자, 아까 <정리>에서 했던 이야기를 다시해보자.
"각각의 바는 다른 종류의 바다. 하지만 그안에들어가는 '속성'은 동일하다. 그러니까 같은속성의 다르게생긴 '바' 라면 당연히 thread를 이용해서 속성을 만들어주고 이를 각각의 바에 대입시켜주면 되는것이다. inflate라는 개념보다는 interface의 개념이 더 가깝지 싶다."
위 thread에서는 "thread를 이용해서 속성을 만들어주고" 를 해결한것이다.
다음으로는 MainActivity에서 "이를 각각의 바에 대입시켜주면 되는것이다" 를 해결하면 된다.
package com.example.mythreadactivity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
//Thread
// - 여러개의 코드를 동시에 실행시킬수 있게 만들수 있는 것...
public class MainActivity extends AppCompatActivity {
// 진행상황을 보여주는 다이얼로그 변수 생성
private ProgressDialog pBar;
// 버튼 생성
private Button wheel,bar;
//(미리 만들어둔) Thread 객체 선언
private ThreadEx threadEx;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wheel = (Button)findViewById(R.id.wheel);
bar = (Button)findViewById(R.id.bar);
wheel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//프로그래스바는 기본적으로 원형을 제공한다..
pBar = ProgressDialog.show(MainActivity.this//Context
,"title"//타이틀
,"Loading..."//메세지
);
//도중 취소 여부
pBar.setCancelable(false);
threadEx = new ThreadEx(handler);
threadEx.start();//Thread 실행 메소드...
}
});
bar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
pBar = new ProgressDialog(MainActivity.this);
//수평 프로그레스바로 설정
pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//메세지 설정
pBar.setMessage("Loading...");
//중도 취소 여부
pBar.setCancelable(true);
pBar.show();
threadEx = new ThreadEx(handler);
threadEx.start();//Thread 실행 메소드...ㄴ
}
});
}
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//인자로 전달된 Message객체로 부터 Bundle 객체를 얻어야 한다...
//Bundle bundle = msg.getData();
//value값 추출
//int value = bundle.getInt("value");
int value = msg.getData().getInt("value");
//받은 정수를 프로그레스 바의 진행값으로 설정...
pBar.setProgress(value);
super.handleMessage(msg);
pBar.setMessage("Loding..." + value + "%");
if(value >= 100){
pBar.dismiss();
threadEx.setCheck(false);
}
}
};
}