본문 바로가기

프로그래밍/Android

특정 쓰레드에서 작업을 수행하고 싶을 때?

안드로이드 프로그래밍을 하다보면 UI 변경은 UI쓰레드에서 작업을 해야만 합니다. 그렇게 하지 않으면 Exception이 발생하게 되죠.

실제로 프로그래밍을 하다보면 서버와의 통신같이 IO가 오래 걸리는 작업은 새로운 쓰레드를 생성해서 작업을 하게 됩니다. 그런데 이 작업 도중에 UI를 변경해야하는 경우도 있죠. 그럴 때 UI를 변경하는 작업만은 UI쓰레드에서 동작하도록 해줘야하죠.

이렇게 UI쓰레드에서 특정 작업을 수행하려면 Activity의 runOnUiThread()메서드를 사용하면 되는데요.
activity.runOnUiThread(new Runnable() {
   @Override
   public void run() {
      // work
   }
});

그런데 이런 메서드를 사용하면서 의문점이 하나 생겼죠.
runOnUiThread() 안에 함수를 넣어주는데 그게 어떻게 UI쓰레드에서 실행되는 것을 보장할까?

이 질문에 대한 답은 샤워를 하다가 문득 떠올랐습니다.


기본 아이디어는 다음과 같습니다.

1. UI쓰레드처럼 나만의 특별한 쓰레드(이하 myThread)가 무한 루프를 돌고 있다.
2. myThread는 ArrayList(이하 myList)를 가지고 있고, myList에는 특정 인터페이스(이하 MyExcutable)를 담는다.
3. 새로운 임의의 쓰레드에서는  myList에 myThread에서 수행하고 싶은 작업(MyExcutable 구현체)을 등록한다.
4. myThread에서 loop을 돌며 myList에 있는 MyExcutable을 수행한다.

무슨 말인지 이해하셨나요??


이해가 잘 안된다면 예제를 들어보겠습니다.

먼저 특정 인터페이스를 정의하겠습니다.

public interface MyExcutable {
   public void excute();
}

그리고 이 인터페이스를 담을 수 있는 List를 만들겠습니다.
 
private List<MyExcutable> myList = new ArrayList<MyExcutable>();




그리고 나서 작업을 등록할 임의의 쓰레드를 생성하겠습니다.
new Thread("temp - thread") {
   @Override
   public void run() {
      while(true) {
         MyExcutable myExcutable = new MyExcutable() {
            @Override
            public void excute() {
               Log.e(TAG, "created in temp-thread");
               Log.e(TAG, "excuting in " + Thread.currentThread().toString());
            }
         };
         myList.add(myExcutable);
					
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
         }
      }
   }
}.start();


이 임의의 쓰레드는 무한 루프를 돌면서 MyExcutable 구현체를 생성하고 이를 myList에 add합니다. 그리고나서 1초간 쉽니다. 
즉 1초마다 myList에 myThread에서 실행하고 싶은 함수들을 추가한다고 보면됩니다.


이제 myThread를 보겠습니다.
new Thread("myThread") {
   @Override
   public void run() {
      while(true) {
         if(myList.size() > 0) {
            MyExcutable myExcutable = myList.get(0);
            myList.remove(0);
            myExcutable.excute();
         }
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
         }
      }
   }
}.start();

myThread 는 무한 루프를 돌며 myList를 체크합니다. myList에 처리할 항목이 있으면 첫번째 항목을 가져오고 myList에서 삭제한 뒤 해당 항목을 실행(excute)합니다. 그리고나서 1초간 쉽니다. 



이렇게 작성한 코드를 디버그 모드로 실행해 보겠습니다. 

아래는 임의의 쓰레드(temp-thread)에서 작업을 생성하는 부분에서 break-point를 건 모습입니다.

 

여기에서 쓰레드 상태를 보면 아래와 같습니다. 주목해서 봐야할 부분은 temp-thread에서 작업이 수행 중이라는 것입니다.

 


계속 실행을 해보면 아래와 같이 myThread의 안으로 들어갑니다.

 
아래의 쓰레드 상태를 보면 현재 break-point가 걸린 부분이 myThread안이라는 것을 알 수 있습니다.




계속 수행해 보겠습니다.

계속 수행하면 아래와 같이 MyExcutable의 excute()안으로 들어오게 됩니다.
즉,  원하는 작업을 수행하게 되는 상황입니다. 이때 이 작업이 temp-thread 에서 이뤄질까요? 아니면 myThread 에서 이뤄질까요?
가만히 들여다보면 분명히 myExcutable을 생성한 곳은 temp-thread입니다. 그래서 temp-thread에서 수행되지 않을까 생각하기 쉽죠.

 

하지만 결과는 아래와 같습니다.

excute()가 myThread에서 동작하고 있음을 알 수 있습니다.




즉, 제 아이디어가 맞다는 것을 의미하죠.

이해가 잘 안된다면 곰곰히 위의 내용을 다시 한번 읽어보시고 곰곰히 생각을 해보시길 바랍니다.^^