안드로이드 프로그래밍[JAVA Code]/Thread

Thread : 스레드 와 핸들러

훈츠 2019. 12. 5. 00:46
반응형

새로운 프로젝트를 만들게 되면 메인 액티비티는 앱이 실행될 때 하나의 프로세스에서 처리된다. 따라서 이벤트 처리나 특정 메서드 기능 구현 할때도 같은 프로세스 안에서 실행된다. 같은 프로세스 안에서 일련의 순서대로 실행 될때, 대부분은 큰문제가 없지만, 대기 시간이 길어지는 네트워크 요청 등의 기능을 수행할 때는 화면에 보이는 UI도 멈춤 상태로 있게 되는 문제가 생길수 있다. 이런 문제를 해결하기 위해 하나의 프로세스 안에서 여러 개의 작업이 동시 수행되는 멀티 스레드 방식을 사용하게 된다. 스레드는 동시 수행이 가능한 작업 단위이다. 하지만 공통 메모리 소스를 나눠 쓰기 때문에 데드락이 발생하여 시스템이 비정상적으로 동작 가능 하다. 데드락이란, 동시에 두 곳 이상에서 요청이 생겼을 때 어떤 것을 먼저 처리할지 판단훌 수 없어 발생 하는 시스템 상의 문제 이다. 이러한 문제를 해결하기 위해 사용 되는것이 핸들러 이다.  

서비스 사용 : 백그라운드 작업은 서비스로 실행하고 사용자에게는 알림 서비스를 이용해 알려줌. 만약 메인 액티비티로 결과값을 전달하고 이를 이용해 다른 작업을 수행하고자 한다면 브로드캐스팅을이용하거나 하여 결과값을 전달 할수 있음.

스레드 사용: 스레드는 동일 프로세스 내에 있기 때문에 작업 수행의 결과를 바로 처리 할 수있음. 그러나 UI 객체는 직접 접근할 수 없으므로 핸들러 객체를 사용함.

표준 자바에서 스레드 사용 방법 : 스레드는 new 연산자를 이용하여 객체를 생성한 후 start()매소드를 호출하면 시작함. Thread 클래스에 정의된 생성자는 크게 파라미터가 없는 경우와 Runnable 객체를 파라미터로 가지는 두가지로 구분함.

Thread 클래스에 정의된 생성자는 크게 파라미터가 없는 경우와 Runnable 객체를 파라미터로 갖는 두 가지가 있다. 

Thread thread1 = new BackgroundThread();
thread1.start();

메인 스레드 : 애플리케이션 객체인 액티비티, 브로드캐스트 수신자 등과 새로 만들어지는 윈도우를 관리하기 위한 메시지큐 를 실행함. 

메시지 큐 : 순차적으로 코드를 수행함.

핸들러 : 메시지 큐를 이용해 메인 스레드에서 처리할 메시지를 전달하는 역활을 담당함.                                                    - 특정 메시지가 미래의 어떤 시점에 실행되도록 스케쥴링 할 수 있음.   

동시 접근의 문제를 핸들러라는걸 이용해서 해결함.  스레드 -> sendMessage <- obtainMessage 를 이용해 메시지 큐에 넣고, 핸들러가 중간에서 메인스레드로 내용을 보내줌. 추후 인터넷 사용하려면 thread를 써야함.

1) 버튼 누르면 1~100 까지 100ms 로 올라가는 기본 스레드 정의 하기 

public class MainActivity extends AppCompatActivity {
    int value = 0;
    TextView textValue;
    Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );
        button =(Button) findViewById( R.id.button );
        textValue = (TextView) findViewById( R.id.textValue );

        button.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BackgroundThread thread = new BackgroundThread(  );
                thread.start();
            }
        } );

    }

    class BackgroundThread extends Thread{
        public void run (){
          for(int i=0 ; i<100 ; i++){
              try{
                  Thread.sleep( 100 );
              } catch ( Exception e){
                  e.printStackTrace();
              }
              value += 1;
              textValue.setText( String.valueOf( value ) ) ;
          }
        }
    }

}

※ 원래 위 코드 처럼 Thread 에서 메인 UI에 직접 접근하면 에러가 발생했었는데, 현재는 문제가 없다. 

핸들러로 메시지 전송하기 

앱을 실행할 때 프로세스가 만들어지면 그 안에 메인 스레드가 함께 만들어 진다. 그리고 최상위에서 관리되는 앱구성요소인 액티비티, 브로드캐스트 수신자 등과 새로 만들어지는 윈도우를 관리 하기 위한 메시지큐(Message Queue)를 실행한다. 메시지 큐를 이용하면 순차적으로 코드를 수행할 수있는데, 이렇게 메시지 큐로 메인 스레드에서 처리할 메시지를 전달하는 역활을 핸들러 클래스가 담당 한다. 결국 핸들러는 실행하려는 특정 기능이 있을 때 핸들러가 포함 되어있는 스레드에서 순차적으로 실행 시킬 때 사용하게 된다. 핸들러를 이용하면 특정 메시지가 미래의 어떤 시점에 실행 되도록 스케쥴링도 할수 있다. 

스레드 #1...n <----- (1) obtainMessage | 메시지 큐 | (핸들러) -> 메인 스레드                                                      스레드 #1...n ------>(2) sendMessage 

1) Handler 정의 

MainHandler handler;
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );
        handler = new MainHandler(  );

2) hander.obtainMessage 메시지 받아오기 -> bundle객체에 데이터담기 -> message안에 bundle객체 담기 -> handler로 메시지보내기 

class BackgroundThread extends Thread{
	public void run (){
		for(int i=0 ; i<100 ; i++){
		try{
			Thread.sleep( 100 );
		} catch ( Exception e){
		e.printStackTrace();
		}
		value += 1;
		Message message = handler.obtainMessage(); //1.obtainMessage
		Bundle bundle = new Bundle(  ); //2.bundle객체 생성
		bundle.putInt( "value",value );//bundle에데이터담기
		message.setData( bundle );//message.set데이터담기
        handler.sendMessage( message );handler로메시지보내기
		}
	}
}

3) MainHandler 클래스 정의 하고 data 처리하기 

class MainHandler extends Handler {
	@Override
	public void handleMessage(Message msg) {
	super.handleMessage( msg );
	
    Bundle bundle = msg.getData(); //msg 에서 bundle 객체 받기
	int value = bundle.getInt( "value" ); // bundle 에서 데이터 뽑기
	textValue.setText( String.valueOf( value ) ) ;//UI 에 쓰기 
	}
}

4) 전체 프로그램 코드 

public class MainActivity extends AppCompatActivity {
    int value = 0;
    TextView textValue;
    Button button;
    MainHandler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );
        handler = new MainHandler(  );

        button =(Button) findViewById( R.id.button );
        textValue = (TextView) findViewById( R.id.textValue );

        button.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BackgroundThread thread = new BackgroundThread(  );
                thread.start();
            }
        } );

    }

    class BackgroundThread extends Thread{
        public void run (){
          for(int i=0 ; i<100 ; i++){
              try{
                  Thread.sleep( 100 );
              } catch ( Exception e){
                  e.printStackTrace();
              }
              value += 1;
              //textValue.setText( String.valueOf( value ) ) ;
              Message message = handler.obtainMessage();
              Bundle bundle = new Bundle(  );
              bundle.putInt( "value",value );
              message.setData( bundle );

              handler.sendMessage( message );
          }
        }
    }

    class MainHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage( msg );
            Bundle bundle = msg.getData();
            int value = bundle.getInt( "value" );
            textValue.setText( String.valueOf( value ) ) ;


        }
    }

}

 결론 : Thread의 새로운 생성은 main Thread에서 구현 했을때 UI 혹은 기타 딜레이를 발생시키는 요소가 있으면 만드는것이 좋다. 서버와 통신 같은것을 할때, request 보내고 데이터를 받는것을 대기 할때 같은 경우에는 반드시 Thread를 이용해야 한다.            

반응형