본문 바로가기

Android

안드로이드(android) 핸들러(Handler) 개념 & 예제

안녕하세요. 

지난번 작성했던 쓰레드와 이어지는 핸들러에 관한 내용입니다.


백그라운드 쓰레드에서 연산작업만 가능하며 다른 쓰레드 소속(UI : 메인쓰레드)의 UI를 건드릴수 없다고 했는데요. 그 이유는 바로 동기화 문제가 발생하기 때문입니다.


예를들어 하나의 텍스트뷰를 두개의 쓰레드에서 수정한다면 아래와 같은 일이 발생할 것입니다.


쓰레드는 병렬적으로 실행되므로 위와 같은 일이 빈번하게 발생할 수 있습니다.


이러한 이유로 백그라운드 쓰레드에서 연산작업은 가능하지만, 메인쓰레드의 UI인 텍스트뷰에 값을 변경할 수 없습니다.

그렇다면 백그라운드에서 연산된 값을 적용하려면 메인쓰레드에 알려 텍스트뷰를 갱신해야하는데 이떄 쓰레드간의 통신을 도와주는게 바로 핸들러(Handler)입니다.


핸들러는 항상 자신을 생성하는 쓰레드에 부착되며 그 쓰레드의 메시지큐를 통해 다른 쓰레드와 통신을 진행합니다. 보통의 경우 다른 쓰레드에서 보낸 메시지를 수신하지만, 자기자신이 보낸 메시지를 수신할 수도 있습니다.

메시지가 도착하면 다음 메소드가 호출되며 메시지를 수신합니다.

void handleMessage(Message msg)


미시지는 쓰레드간 통신내용을 저장하는 객체이고 단순한 신호, 명령 뿐만 아니라 복착한 추가정보도 전달받을 수 있습니다.


필드 

설명 

int what 

메시지의 의미를 설명한다. 의미가 정해져 있지는 않으며 핸들러별로 지역적이므로 다른 핸들러와 충돌할 위험은 없다. 

int arg1 

메시지의 추가정보 

int arg2 

메시지의 추가정보 

Object obj 

정수만으로 메시지를 기술할 수 없을때 임의의 객체를 전달한다 

Messenger replyTo 

메시지에 대한 응답을 받을 객체를 지정한다.


메시지를 보내는 쪽에서 전달하고자 하는 내용을 Message객체에 저장하여 핸들러로 전송하는데, 이때 아래 메소드를 사용한다.

boolean Handler.sendEmptyMessage(int what) : 간단하게 what 값을 통해서 메시지를 보낼 때 사용

boolean Handler.sendMessage(Message msg) : 좀더 복잡한 Message객체를 보낼 때 사용

boolean sendMessageAtFrontOfQueue(Message msg) : 메시지는 큐에 순서대로 쌓여 처리되나 급하게 처리해야할 메시지를 우선적으로 지정할때 사용


위와 같이 메시지를 보내게 되면 핸들러의 handleMessage 메소드가 호출되고 메시지를 수신받게 됩니다. 이때 필요에 따라 각 메시지별로 구현을 다르게 할 수 있습니다.


지난번에 공부했던 쓰레드에 핸들러를 적용해보겠습니다.

쓰레드만 있을 때엔 UI변경이 불가능해 버튼을 눌렀을 때에만 백그라운드 쓰레드에서 연산된 값을 업데이트 했었는데요 이번 예제에선 핸들러를 이용해 값을 업데이트 해보겠습니다.

class BackRunnable implements Runnable{
		public void run(){
			while(true){
				backValue++;
				mHandler.sendEmptyMessage(0);
				//backText.setText("BackValue : "+backValue);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	Handler mHandler = new Handler(){
		public void handleMessage(Message msg){
			if(msg.what == 0){
				backText.setText("BackValue : "+backValue);
			}
		}
	};


위에는 runable객체에서 mHandler.sendEmptyMessage(0)을 통해 핸들러에 0이란 what값을 전달하고 handleMessage메소드에서 해당 메시지를 받아 처리하는 소스입니다.


이처럼 구현을 한다면 백그라운드 쓰레드는 연산하여 메인쓰레드에 부탁을 하지만, 자신이 원할 때 값을 출력할 수 있는 셈입니다. 모든 UI의 변경이 메인쓰레드로 일원화되어 동기화문제가 발생될 여지가 없어지는 셈이죠


자 그럼 디바이스에서 돌리면 어떻게 될까요?




증가 버튼을 누르지 않고도 BackValue가 증가하는 것을 확인할 수 있습니다.


이번엔 sendEmptyMessage가 아닌 sendMessage를 사용해보도록 하겠습니다.

class BackRunnable implements Runnable{
		public void run(){
			while(true){
				backValue++;
				Message msg = new Message();
				msg.what = 0;
				msg.arg1 = backValue;
				mHandler.sendMessage(msg);
				//backText.setText("BackValue : "+backValue);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	Handler mHandler = new Handler(){
		public void handleMessage(Message msg){
			if(msg.what == 0){
				backText.setText("BackValue : "+msg.arg1);
			}
		}
	};

message객체를 만들어 what과 arg1을 셋팅하였고, mHandler로 sendMessage메소드를 통해 전달하였습니다.


위와같이 msg를 통해 좀더 구체적인 데이터를 전달할 수 있으며 obj를 이용하면 정수만 전달할 수 있는 arg1,2보다 더욱 자세한 데이터를 전달 할 수 있을 것 같습니다.


핸들러가 생각보다 공부할 내용이 많은데요, 다음번에도 핸들러 2부를 포스팅하도록 하겠습니다.


handler_test.zip