본문 바로가기

프로그래밍/Android 짜투리 지식

[안드로이드] handler의 사용

handler 사용 요약
[선언]
public Handler mHandler = new Handler(){ // 핸들러 처리부분
	public void handleMessage(Message msg){ // 메시지를 받는부분
		switch(msg.what){ // 메시지 처리
			case 1:
				set_timer(); // 나는 함수를 호출하였다.
				break;
			case 2:  // 기타 전달인자로 인한 처리도 가능
				break;
			case 3:
				break;
		}
	};
};
[호출]
handler.sendMessage(handler.obtainMessage(what, arg1, arg2));
what : Message 구분자
arg1 : param 1
arg2 : param 2

다음과 같이 호출하면 생성부의 handleMessage로 가서 처리하게 된다.

thread 사용 할 때 한번 더 생각해 볼 내용 :
이 method가 UI를 처리하나, 단순 자료를 처리하나
UI의 처리는 UI Thread
- View.post
- handler
- new Handler(new Runnable());
AsyncTask().onPostExecute();
자료 처리는 MainThread(간단한 처리..만) 또는 child Thread
    - Thread
   - AsyncTask().doInBackground();

이걸 한번에 묶어서 처리 할 수 있는것이 android 의 AsyncTask()
onPreExecute();
  - setter
doInBackground();
  - data 처리
onPostExecute();
  - ui 처리
로 사용하면 되겠다.
시도 :
화면에 Textview를 이용하여 타이머를 설정하려고 시도.
생각 : 
1. 화면은 화면의 흐름을 따르고, 타이머는 화면과 별도로 동작해야한다.
2. 그럼 타이머는 따로 놀아야한다.
3. Thread라는 개념이 있다. Thread는 동시작업하기에 용이하다.
(Thread의 개념은 대략 생각이 안나는데.. 그냥 멀티테스킹이라는 개념만이 생각날뿐..)
4. Thread를 사용하자.
5. 안드로이드에서는 어떻게 사용하면되지.
6. Thread는 생성, 시작, 내용이 필요하다.
구현 :
       Thread
        - Thread thread = new Thread();  // Thread의 생성
        - thread.start()                           // Thread의 시작
        - public void run(){}                   // Thread의 내용
       
내용 :
Thread pThread = new Thread(new Runnable(){
	@Override
	public void run(){
		try{
			while(!clicked_reset_btn){
				//Toast.makeText(sun_handler.this, "blink", 1000).show();
				textv.setText(Integer.toString(tSecond));
				tSecond = tSecond +1;
				Thread.sleep(1000);
			}
 
			if(clicked_reset_btn){
				tSecond = 0;
			}
		}catch (Throwable t) {
			//	Toast.makeText(sun_handler.this, "D", Toast.LENGTH_SHORT).show();
			//	TODO: handle exception
		}
	}
});

하니까 안되더라.
쓰레드 진입은 하는데 한번만 하고 바로 catch문으로 들어가 버렸다!!!
그래서 물어봤다. (androidside http://www.androidside.com)에

다른쓰레드에서 UI쓰레드로 접근하는 부분은 임계영역입니다.
textv.setText(Integer.toString(tSecond));
여기서 하지 말고 UI쪽으로 메시지를 보내서 핸들링하는 구조로 변경을 하세요.


라고 하셨다.
내용은 메인쓰레드에서 하지말고 서브쓰레드를 생성해서 작성하라 하셨다.

즉 메인 쓰레드(UI 쓰레드)에서 자식쓰레드를 생성하고 그걸 run시키는 방법.
도구는 handlermessage를 이용하여서.

수정 :

public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	Thread time_thread = new Thread(this); //자식쓰레드 생성
	time_thread.start(); // 쓰레드 시작
}

public void run(){
	while(!STARTED & !CLICKED_RESET_BUTTON){
		try{
			Thread.sleep(1000); // 1초의 딜레이
		}
		catch (InterruptedException e) {
			e.printStackTrace();
			// TODO: handle exception
		}
		mHandler.sendMessage(Message.obtain(mHandler, 1)); // handler로 메시지를 보내서
			// 처리함. 처리번호는 1.  메시지 처리부는 Message.obtain()의 두번째 인자를 switch문을
			// 이용하여 처리한다.
		}
	}

public Handler mHandler = new Handler(){ // 핸들러 처리부분
	public void handleMessage(Message msg){ // 메시지를 받는부분
		switch(msg.what){ // 메시지 처리
			case 1:
				set_timer(); // 나는 함수를 호출하였다.
				break;
			case 2:  // 기타 전달인자로 인한 처리도 가능
				break;
			case 3:
				break;
		}
	};
};
고찰 :
handler의 message처리

1. 자식쓰레드생성

- Activity는 하나의 Thread를 가진다.
2. 쓰레드 내용 작성
- run()함수의 작성
3. 핸들러를 이용하여 UI에 메시지 전달(run()의
mHandler.sendMessage(Message.obtain(mHandler, 1));
4. 핸들러 처리
- handleMessage(Message msg)로 처리


성공적으로 타이머를 완성시켰다. 메인쓰레드와는 별도로 서브 스레드를 생성하여 작성하였다.

아.. 질문하신 내용은 사실 내용이 상당히 많은 데요. 중요한 것만 요약해서 답변 드릴께요. ^^
UI는 Activity를 상속한 클래스가 인스턴스화되면서 하나의 쓰레드가 생기는데요.
그곳에서 다른 쓰레드를 생성하면 자식쓰레드가 되겠죠.
UI가 돌고 있는 쓰레드에 속하거나 힙에 인스턴스화된 공간에 다른(자식) 쓰레드에서 접근하는 것은 문제가 생기죠.
두개이상의 쓰레드에서 같은 공간을 동시에 접근할 가능성이 있기 때문입니다.(임계영역)

그래서 메시지큐를 이용합니다.

메인쓰레드(UI)에 핸들러를 구현하고 자식쓰레드에 인자로 넘깁니다.
자식쓰레드는 핸들러에 sendMessage...()합니다.
그러면 안드로이드 운영체제에서 스케쥴링중에 메인쓰레드의 메시지큐에 넣어 줍니다.
UI쪽에선 큐에 있는 내용을 하나씩 꺼내어 작업을 하죠.

결국 사용자가 버튼을 터치하는 등의 작업을 할 때도 똑같은 상황이 되어서
임계영역이 생기지 않습니다. 순차적으로 동작이 된다는 것이지요.

위는 서브스레드의 생성과 핸들러의 메시지 처리에 대한 안드로사이드의 히스껌님의 답변내용^^ 이해하는데 있어서 상당한 도움을 받았다! 감사합니다 ^^