기록장
[안드로이드] Thread, Handler 이해 및 간단한 예제 본문
들어가기 전에..
내 생각
처음 접하시는 분들은 이걸 왜 써야하는지 잘 모르고 접근하면 이해가 더 안될 것이라 생각되어 사용 목적에 대해 간략이 적어보자면,, (나도 배우는 입장이라 잘 모른다만.. 아는 거 최대한 써봄) 쓰레드는 네트워킹, 작업 처리에 있어서 매우 중요한 개념이다. 오래걸리는 작업이 붙잡고 있다면 UI가 멈춰 기다려야할 수 있다. 작업 끝날 때까지 화면에 제한이 있다면.. 좋지 못할 것이다. 또한, 다른 웹 서버에서 가져온 정보들을 UI에 보여주기 위해서는 인터넷 기능을 사용해야한다. 안드로이드는 인터넷을 사용하는데 있어서 무조건 쓰레드를 사용한 환경 내에서 진행하여야 하는데 임의로 만든 쓰레드에서 가져온 데이터가 UI에 접근하기 위해서 메인 쓰레드로 보내준 다음 사용하여야 한다. 이것들을 잘 다루기 위해 이 사이에 필요한 개념들(핸들러, 쓰레드 사용법, 개념 등)을 잘 익혀둬야 한다고 생각한다.
우리가 액티비티를 실행시킬 때 일어나는 모든 일들은 하나의 프로세스에서 처리된다. 하나의 프로세스에서 처리되는 것은 평소에는 큰 문제가 없지만 오래걸리는 작업이 있을 경우 그 뒤로 쌓이는 작업들은 어떻게 처리해야할지 고민해볼 필요가 있다.
쓰레드는 동시 수행이 가능한 작업 단위로, 현재 수행 중인 작업 이외의 기능을 동시에 처리할 때 새로운 쓰레드를 만들어서 처리해준다. 이 방식을 멀티 쓰레드 방식이라고 한다. 멀티 쓰레드는 메모리 리소스를 공유하므로 효율적인 처리가 가능하다. 그러나 동시에 두 곳 이상에서 작업 요청이 생겼을 경우 우선순위가 없다면 교착상태(DeadLock)에 빠질 수 있다.
안드로이드에서는 UI를 처리할 때 사용되는 기본 쓰레드를 메인 쓰레드라 부른다. 메인 쓰레드 이외에 다른 쓰레드에서 UI에 직접 접근이 불가능하다. 그럼 메인 쓰레드 이외의 쓰레드에서 수행된 작업은 UI 처리를 못해줄까? 그래서 존재하는 것이 Handler(핸들러) 객체이다. 핸들러는 UI에 접근하려는 생성된 쓰레드에서 메시지를 전달받아 메인 쓰레드에서 처리할 수 있도록 만들어 준다.
핸들러의 역할
메시지 큐를 이용해 메인 쓰레드에서 처리할 메시지를 전달하는 역할을 담당
특정 메시지가 미래의 어떤 시점에 실행되도록 스케줄링 가능
핸들러를 사용할 때 필요한 3가지 단계가 있다.
- obtainMessage(): 메시지 객체 반환
- sendMessage(): 메시지 큐에 넣음 (핸들러로 메시지 객체를 보냄)
- handlerMessage(): 들어온 메시지 큐를 순서대로 처리, 메인 쓰레드가 수행되는 위치에 있음
* 메시지 큐(Message Queue): 최상위에서 관리되는 앱 구성 요소인 액티비티, 브로드캐스트 수신자 등과 새로 만들어지는 윈도우를 관리
코드
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=".MainActivity"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="텍스트"
android:textSize="30dp"
android:layout_marginBottom="100dp"
android:id="@+id/textview1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="버튼"
android:id="@+id/button1"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button b1;
TextView tv1;
int value = 0;
Handler1 handler1 = new Handler1(); //만들어준 Handler1에 대한 객체 생성
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = findViewById(R.id.textview1);
b1 = findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
thread_class thread1 = new thread_class(); //쓰레드 클래스 객체 생성
thread1.start(); //쓰레드 시작
}
});
}
class thread_class extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<100;i++){
try {
Thread.sleep(1000); //1초마다 쓰레드 sleep
} catch (Exception e){
}
value += 1;
Message message = handler1.obtainMessage(); //메시지 객체 반환
Bundle bundle = new Bundle();
bundle.putInt("value",value);
message.setData(bundle); //Bundle에 담긴 데이터 메시지에 담고
handler1.sendMessage(message); //핸들러로 메시지 객체를 보내줌 (메시지 큐에 넣음)
}
}
}
class Handler1 extends Handler{
@Override
public void handleMessage(@NonNull Message msg) { //메시지 처리
super.handleMessage(msg);
Bundle bundle = msg.getData();
int value = bundle.getInt("value");
tv1.setText("Value: " + value);
}
}
}
결과는 handleMessage() 메소드에서 텍스트에 업데이트 해줌으로써 1초마다 숫자가 100까지 올라갈 것이다.
위에 코드보다 조금 더 간편하게 쓰레드를 생성할 수 있는 방법으로 Runnable 객체가 있다. Runnable 객체를 핸들러의 post() 메서드로 전달해주면 이 객체에 정의된 run() 메서드 안의 코드들은 메인 쓰레드에서 실행된다. 코드를 보면 차이를 확실히 알 수 있다.
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button b1;
TextView tv1;
int value = 0;
Handler handler = new Handler(); //Handler 객체 생성
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = findViewById(R.id.textview1);
b1 = findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
thread_class thread1 = new thread_class(); //쓰레드 클래스 객체 생성
thread1.start(); //쓰레드 시작
}
});
}
class thread_class extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<100;i++){
try {
Thread.sleep(1000); //1초마다 쓰레드 sleep
} catch (Exception e){
}
value += 1;
handler.post(new Runnable() { //핸들러의 post 메소드로 전달했기 때문에 메인 쓰레드 실행이 된다.
@Override
public void run() {
tv1.setText("Value: "+ value);
}
});
}
}
}
}
결과는 같다.
표준 Thread를 사용한 것과 Runnable 객체를 사용한 것에 대한 성능 차이는 없다. 본인이 편한대로 사용하면 된다.
쓰레드를 사용하는데 있어서 알아두면 좋은 메소드
표준 쓰레드
- public boolean sendMessageAtTime(Message msg, long uptimeMillis): 메시지를 보낼 때 시간을 지정
- public boolean sendMessageDelayed(Message msg, long delayMillis): 일정 시간 지난 후 실행
Runnable
- postAtTime(): sendMessageAtTime()과 같음
- postDelayed(): sendMessageDelayed()와 같음
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button b1;
TextView tv1;
Handler handler = new Handler(); //Handler 객체 생성
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = findViewById(R.id.textview1);
b1 = findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
request();
}
});
}
private void request(){
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("원격 요청");
builder.setMessage("데이터를 요청하시겠습니까?");
builder.setIcon(R.drawable.ic_launcher_foreground);
builder.setPositiveButton("예", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
tv1.setText("5초 후에 결과 표시됨");
handler.postDelayed(new Runnable() { //일정시간 후 실행
@Override
public void run() {
tv1.setText("요청 완료됨");
}
}, 5000); //5초 딜레이
}
});
builder.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
tv1.setText("취소됐음");
}
});
builder.show();
}
}
결과
-> 예
-> 아니오
DiaAlertDialog에 대해 잘 모르겠다면 참고
[안드로이드] AlertDialog 클래스 / 알림창, 대화상자 띄우기
자주 사용되는 알림 대화상자는 사용자에게 확인을 받거나 어떤 것을 선택하게 할 때 사용한다. 큰 틀로 보자면 대화상자 생성 -> 용도에 따른 설정 -> 대화상자 화면 출력 순으로 진행된다. 그럼
edit0.tistory.com
부족한 점, 피드백 환영합니다.
감사합니다.
참고
정재곤, 『Do it! 안드로이드 앱 프로그래밍 - 개정 6판』, 이지스퍼블리싱(주)
'안드로이드' 카테고리의 다른 글
[안드로이드] 네트워킹, Socket (소켓) (0) | 2021.01.07 |
---|---|
[안드로이드] AsyncTask 이해 및 간단한 예제 (0) | 2021.01.07 |
[안드로이드] Drawable 드로어블 (0) | 2021.01.05 |
[안드로이드] Fragment 이해하기(4) / NavigationDrawer 네비게이션 드로어 (0) | 2021.01.04 |
[안드로이드] Fragment 이해하기(3) / ViewPager 뷰페이저 (0) | 2021.01.04 |