어플리케이션을 위해서 프로세스가 하나 만들어 지면 메인 스레드에서는 최상위에서 관리되는 어플리케이션 객체인 액티비티와 브로드 캐스트 수신자 등 새로운 윈도우를 관리하기 위한 '메시지 큐'를 실행하게 됩니다.
이 '메시지 큐'를 이용해서 순차적으로 코드를 수행할 수 있고, 이렇게 '메시지 큐'를 이용해 메인 스레드에서 처리할 메시지를 전달하는 역할을 하는 것이 바로 '핸들러 클래스'입니다.
위의 그림에서 보이 듯이 새로만든 스레드 (스레드#1)가 수행하려는 정보를 메인 스레드로 전달하기 위해서는 먼저 핸들러가 관리하는 '메시지 큐'에서 처리할 수 있는 메시지 객체 하나를 참조해야 합니다. 객체 하나를 참조하기 위한 방법으로는 obtainMessage() 메소드를 이용할 수 있고 호출의 결과로 메세지 객체를 리턴 받게 됩니다.
이 메시지 객체에 필요한 정보가 들어간 뒤, sendMessage() 메소드를 이용해 '메시지 큐'에 넣을 수 있습니다. '메시지 큐'에 들어간 메시지는 순서대로 핸들러가 처리하게 되며 핸들러 내의 메소드 handleMessage() 메소드에 정의된 기능이 수행됩니다. 이 handleMessage() 메소드에 들어있는 코드가 실행되는 위치는 '메인 메소드'라는 것은 필수로 알고 있어야 합니다.
그렇다면 직접 코드를 보고 이해를 해보도록 합시다.
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="match_parent"
tools:context="com.example.chanheum.samplethread.MainActivity"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textColor="#ff000000"
android:textStyle="bold"
android:textSize="20dp"
android:text="Working ..."/>
<ProgressBar
android:id="@+id/progress"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"/>
</LinearLayout>
간단하게 스레드-핸들러가 동작하는 것을 보기 위해서 레이아웃은 신경쓰지 않고 기본적으로 확인하기 위한 기능들만 구현했습니다.
여기서 알아야 할 점은 레이아웃을 구현하는 데에 있어서 ProgressBar는 처음에 bar형태가 아닌 둥글게 돌아가는 형태 이기 때문에
style="?android:attr/progressBarStyleHorizontal"
을 꼭 추가해서 bar형태로 바꿔 주어야 합니다.
2. Activity.java
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
ProgressBar bar;
TextView textView;
boolean isRunning = false;
ProgressHandler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bar = (ProgressBar) findViewById(R.id.progress);
textView = (TextView) findViewById(R.id.textView);
handler = new ProgressHandler();
}
public void onStart() {
super.onStart();
bar.setProgress(0);
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
for (int i = 0; i < 20 && isRunning; i++) {
Thread.sleep(1000);
Message msg = handler.obtainMessage();
handler.sendMessage(msg);
}
} catch (Exception e) {
Log.e("MainActivity", "Exception in progressing message", e);
}
}
});
isRunning = true;
thread1.start();
}
public void onStop(){
super.onStop();
isRunning = false;
}
public class ProgressHandler extends Handler{
public void handleMessage(Message msg){
bar.incrementProgressBy(5);
if(bar.getProgress() == bar.getMax()){
textView.setText("Done");
} else {
textView.setText("Working...." + bar.getProgress());
}
}
}
}
위의 코드는 스레드와 핸들러를 따로 구분하여 Handler 클래스를 상속한 ProgressHandler 클래스를 새로 정의한 형태입니다.
그 이유는 Handler 클래스에 들어있는 handleMessage() 메소드를 다시 정의해서 메시지가 메인 스레드에서 수행될 때 필요한 기능을 정의하기 위해 이렇게 구분을 해놓은 것입니다. 스레드 안에 있는 run() 메소드안에 있는 코드가 스레드의 시작 시에 수행된다는 것은 알고 있을 겁니다. 여기서 obtainMessage()로 메세지 객체 하나를 참조한 뒤 이 객체를 sendMessage() 메소드를 사용하여 메시지 큐에 넣게 됩니다.
sendMessage()로 메시지 큐에 전달이 되면 handleMessage() 메소드 안의 코드가 실행되는 원리입니다. 위의 코드는
메시지가 메시지 큐에 전달 될 때마다(스레드가 실행될 때마다) 메인 액티비티에서 Progressbar가 5%씩 증가하여 TextView에 Working...과 프로그레스바가 꽉차게 되면 Done을 출력하는 원리입니다.