ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [안드로이드] Thread and parallelism - 1
    앱개발/안드로이드 앱 개발 2018. 4. 3. 23:11
    Thread-and-parallelism.md

    Udacity Android Basics: Networking - Threads & Parallelism 발췌

    Thread and parallelism - 1

    안드로이드에서도 당연히 쓰레드를 지원한다. 기본적으로 메인 쓰레드 하나에서만 동작을 하고 있지만 원하는 경우 언제든지 백그라운드 쓰레드를 추가하여 메인 쓰레드와 비동기적으로 다른 작업을 수행하도록 만들 수 있다. 보통 네트워크 작업이나 데이터베이스 저장 작업과 같이 UI와 관련 없는 일들을 백그라운드 쓰레드로 작업한다. 쓰레드에 관한 자세한 내용은 이 문서 를 참조하자.

     

    네트워크 작업은 백그라운드에서 수행하자

    이 github 리포지토리starting-point 브랜치를 다운로드 한뒤 안드로이드 스튜디오를 통해 실행해보자. 그러면 애플리케이션이 실행되지 않고 에러가 나오는 것을 볼 수 있다. 안드로이드 스튜디오의 logcat을 통해 살펴보면, NetworkOnMainThreadException 예외로 인해 실행하지 못함을 확인할 수 있다. 이것은 예외 이름 그대로 메인 쓰레드에서 네트워크 작업을 수행하려고 시도했기 때문이다.

    네트워크 작업은 서버에 연결, 데이터 전송/요청, 응답 데이터 처리, 응답 발송 등과 같이 다양하고 오래걸리는 작업을 수반하는게 일반적이다. 이러한 작업을 백그라운드 쓰레드가 아닌 메인 쓰레드에서 진행을 하게 되면, UI 관련 작업을 수행해야 하는 메인 쓰레드가 네트워크 관련 작업이 모두 종료될 동안 아무것도 하지 못한다. 이런 경우 앱이 정지된 것과 같은 모습을 보일 수 있으므로 매우 비효율적이기 때문에, 네트워크 작업과 같은 오래 걸리는 작업들은 반드시 백그라운드 쓰레드를 통해 수행해야 한다.

     

    AsyncTask 클래스를 사용한 백그라운드 쓰레드

    안드로이드 개발 환경에서는 AsyncTask라는 추상 클래스 (abstract class)를 제공한다. AsyncTask를 상속하여 사용하기 위해서는 doInBackground() 메소드를 구현해야 한다. 메소드 이름에서 볼 수 있듯이 백그라운드에서 작업할 내용을 코딩하면 된다.

    백그라운드에서 필요한 작업을 수행한 뒤 UI를 업데이트 하고 싶을 경우에는 메인쓰레드에서 UI 업데이트를 수행해야 한다. 그러나 AsyncTask.doInBackground() 의 경우 백그라운드에서 수행되므로 해당 메소드 내에 UI 업데이트 코드를 작성해서는 안된다. 이를 지원하기 위해 AsyncTask 클래스 내에서 onPostExecute() 메소드를 제공한다. 해당 메소드는 doInBackground() 메소드 수행이 완료된 뒤 자동적으로 호출 된다.

    어떤 경우에는 백그라운드 작업을 수행하기 전에 메인 쓰레드에서 특정 작업을 먼저 수행한 뒤 그에 따라서 백그라운드 작업이 달라지길 원하는 경우가 있을 수 있다. 그럴 때 사용할 수 있도록 onPreExecute() 메소드를 제공한다. onPostExecute() 메소드와 마찬가지로 메인쓰레드에서 동작하므로 UI 업데이트 작업또한 수행할 수 있다.

    백그라운드 작업을 통해 파일 다운로드 등과 같이 오래 걸리는 작업을 수행할 경우, UI 업데이트를 통해 현재 진행상황을 알려주고 싶은 경우가 있다. 이럴 때 doInBackground() 메소드에서는 UI 작업을 수행하면 안되므로, 이를 해결하기 위해 onProgressUpdate() 메소드를 제공한다. doInBackground() 메소드 내에서 언제든지 원하는 경우 호출할 수 있으며 메인쓰레드에서 실행되므로 UI 업데이트도 진행할 수 있다. 실행은 publishProgress() 메소드 호출을 통해 진행한다.

     
    public class EarthquakeAsynctask extends AsyncTask {
      // ... 기타 다양한 코드나 변수 선언
      
      void onPreExecute() {
        // 백그라운드 작업을 수행하기 전에 메인쓰레드에서 처리할 코드
      }
      
      // Result 오브젝트는 예시를 위한 임시 이름이다. 
      // 반환 값으로 사용하길 원하는 오브젝트 이름으로 대체하면 된다.
      Result doInBackground() {
        // 백그라운드에서 작업할 코드 
        // ...
        
        // 백그라운드 작업 도중 진행 상황을 알려주고 싶을때
        publishProgress(73);
        
        return result;
      }
      void onPostExecute(Result result) {
        // 백그라운드 동작이 끝난 뒤 수행할 코드
        // 각종 UI 업데이트 코드를 이곳에 작성하면 된다. 
      }
      
      void onProgressUpdate(Progress p) {
        // 메인 쓰레드에서 동작한다. 
        // 백그라운드 쓰레드 동작 도중 호출 할 수 있다. 
      }
    }

     

    예제

     
    x
     private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
         protected Long doInBackground(URL... urls) {
             int count = urls.length;
             long totalSize = 0;
             for (int i = 0; i < count; i++) {
                 totalSize += Downloader.downloadFile(urls[i]);
                 publishProgress((int) ((i / (float) count) * 100));
                 // Escape early if cancel() is called
                 if (isCancelled()) break;
             }
             return totalSize;
         }
         protected void onProgressUpdate(Integer... progress) {
             setProgressPercent(progress[0]);
         }
         protected void onPostExecute(Long result) {
             showDialog("Downloaded " + result + " bytes");
         }
     }

    위의 예제는 안드로이드 공식 문서에서 제공하는 예제이다. 백그라운드 태스크를 생성한 이후에는 new DownloadFilesTask.execute(url1, url2, url3) 와 같이 execute 메소드를 통해서 수행할 수 있다.

    AsyncTask를 상속할 때 <>에 전달되는 파라미터들은 순서대로, doInBackground() 메소드에서 전달 받을 매개변수의 오브젝트 네임, onProgressUpdate()에서 전달받을 매개변수의 오브젝트 네임, onPostExecute()에서 전달받을 매개변수의 오브젝트 네임이다. 주의해야 할 점은 파라미터 자리에는 항상 오브젝트가 들어가야 하므로, int, long, void 등과 같은 원시 자료형을 사용하고자 하는 경우에는 그에 맞는 Integer, Long, Void 등과 같이 오브젝트 타입을 사용해야 한다.

    첫 파라미터인 URLdoInBackground(URL... urls)를 보면 확인할 수 있다. URL...처럼 오브젝트 네임 뒤에 ...이 붙은 경우, varargs 라는 특수한 매개변수임을 포현한다. 이는 전달되는 변수가 0개 이상 (0,1,2,3 ...) 이 올 수 있음을 의미하며 urls는 항상 배열 형태를 유지한다.

    두번째 초기화 파라미터인 LongdoInBackground() 메소드의 반환값이자 onPostExecute(Long result) 메소드의 매개변수를 나타낸다.

    세번째 초기화 파라미터인 IntegeronProgressUpdate(Integer... progress) 메소드의 매개변수이다. 이 메소드 또한 ...을 통해서 전달되는 변수가 0개 이상이고, progress가 항상 배열 형태임을 알 수 있다. onProgressUpdate() 메소드는 직접적으로 호출해서는 안되고 이미 구현되어 있는 publishProgress() 메소드 호출을 통해 수행하여야 한다.

Designed by Tistory.