본문 바로가기

Android

안드로이드(android) 핸들러(Handler) 예제2

안녕하세요 코코콩입니다.


지난번에 메시지를 핸들러로 보내 작업을 처리하는 부분을 포스팅했었는데요

이번 포스팅에선 지난번에 한 메시지를 핸들러로 보내는 예제를 Runalbe객체를 보내 실행하는 방법으로 변경하는 것을 알아보겠습니다.


class BackRunnable extends Thread{
		public void run(){
			while(true){
				backValue++;
				Message msg = new Message();
				msg.what = 0;
				msg.arg1 = backValue;
				mHandler.sendMessage(msg);
				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);
			}
		}
	};

<메시지를 통해 what과 arg1을 핸들러로 전달, 텍스트뷰에 표시>



class BackRunnable extends Thread{ public void run(){ while(true){ backValue++; mHandler.post(new Runnable(){ public void run(){ backText.setText("BackValue : " + backValue); } }); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } Handler mHandler = new Handler();

<post메소드로 Runnalble객체를 전달해 바로 실행하는 소스 >


boolean post(Runnable r) 

간단한 작업의 경우 메시지를 보내는 것보다 post를 통해 실행하는 것이 훨씬 더 간단하다.


핸들러는 메시지를 직접적으로 처리하지 않으므로 handleMessage메소드가 필요가 없어 정의하지 않았으며, post를 할 핸들러만 정의해주면 됩니다.

(만약 메시지도 처리하고 Runnable도 필요하다면 핸들러에 handleMessage를 정의하면 됨)


주의할 점은 러너블 객체를 생성하는 소스가 백그라운드 쓰레드에 있지만 이 러너블 객체를 실행하는건 다름 아닌 메인쓰레드라는 점인데요,


백그라운드 쓰레드에서 러너블 객체를 보내면, 일단 메인쓰레드 큐에 저장되었다가 차례가 되면 핸들러에 의해 꺼내지게 되며 동작하게 됩니다.


위 러너블 코드가 이해가 잘 안가신다면 아래와 같이 변형하면 더 쉽게 느껴지실겁니다.


class BackRunnable extends Thread{
	public void run(){
		while(true){
			backValue++;
			Runnable runnableTest = new Runnable(){
				public void run(){
					backText.setText("BackValue : "+ backValue);
				}
			};
			mHandler.post(runnableTest);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
	
Handler mHandler = new Handler();


그러나 위의 예제들은 모두 한계가 있습니다.

그 이유는 바로 쓰레드가 Activity 안에 구현되어있기 때문인데요


만약 쓰레드를 여러 Activity에서 공유해야한다면? 특정 Activity에 쓰레드를 정의하는 건 바보같은 일일 것입니다.


아래 소스는 쓰레드를 액티비티 외부 클래스로 정의한 코드입니다.


public class MainActivity extends Activity {
	
	int mainValue = 0;
	TextView mainText;
	TextView backText;
	Button increaseButton;
	BackThread bThread;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mainText = (TextView)findViewById(R.id.mainValue);
		backText = (TextView)findViewById(R.id.backValue);
		increaseButton = (Button)findViewById(R.id.increase);

		bThread = new BackThread(mHandler);
		bThread.setDaemon(true);
		bThread.start();
		
		increaseButton.setOnClickListener(new OnClickListener(){
			@Override
			public void onClick(View arg0) {
				mainValue++;
				mainText.setText("MainValue : "+mainValue);

			}
		});
	}
	
	//Handler mHandler = new Handler();
	
	Handler mHandler = new Handler(){
		public void handleMessage(Message msg){
			if(msg.what == 0){
				backText.setText("BackValue : "+msg.arg1);
			}
		}
	};
}

class BackThread extends Thread{
	int backValue = 0;
	Handler mHandler;
	BackThread(Handler handler){
		mHandler = handler;
	}
	public void run(){
		while(true){
			backValue++;
			Message msg = new Message();
			msg.what = 0;
			msg.arg1 = backValue;
			mHandler.sendMessage(msg);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


BackThread를 외부 클래스로 빼는 동시에 Runnable객체를 쓰레드를 상속받도록 수정하였습니다.


이제 BackThread가 외부클래스로 바뀌었으니 앞의 예제들과 구조가 달라집니다.

1. backValue를 연산해야하므로 Activity소속의 변수에서 쓰레드 소속의 변수가 되어야한다.

2. 메인쓰레드는 백그라운드 쓰레드의 신호(메시지)를 받아야 하므로 핸들러를 전달한다. 백그라운드 쓰레드는 전달받은 핸들러를 자신의 멤버로 저장한다.

3. 메인쓰레드는 backValue를 직접 참조할 수 없으므로 message객체를 통해 값을 전달받는다.


코드가 좀 더 복잡해졌지만, 이로써 BackThread를 다른 Activity에서 참조가 가능해졌습니다.


만약 여러 Activity에서 참조해야한다면 위의 코드를, 그게 아니라 하나의 Acitivity에서 사용한다면 처음의 코드가 편리하겠죠?



다음번엔 메시지와 루퍼에 관해 알아 보도록 하겠습니다.


오늘은 이만...