본문 바로가기
Android/Dev

AsyncTask, Coroutine, RxJava를 이용한 비동기의 구현

by featherwing 2020. 3. 12.
반응형
  1. 비동기를 구현하는 이유
  2. AsynkTask
  3. RxJava
  4. Coroutine

 


안드로이드의 어플리케이션은 UI 쓰레드 라고 하는 메인쓰레드가 UI(각종 버튼, 리스트등)를 관리하고 처리합니다.

이 메인쓰레드는 유저의 앱 사용성에 직결된 요소이기 때문에 항상 막힘없이 처리되어야 합니다.

이 때문에, 메인쓰레드를 통해서 할 수 있는 작업에는 안드로이드 OS가 제약을 두고 있습니다.

 

간단한 예를 들면, 네트워크를 사용하여 정보를 받아 이를 업데이트 하는 작업이 있다고 가정하고,

이를 메인쓰레드에서 수행하려고 하면 NetworkOnMainThreadException을 뿜어내며 앱이 종료되는것을 확인할 수 있습니다.

 

때문에 비동기를 구현해서 네트워크작업을 처리해주어야 하고, 이를 위한 여러가지 방법들이 존재하는데,

이때 가장 간단하게 사용되는 것이 AsyncTask 입니다.

AsyncTask는 각종 안드로이드 입문서등에서 비동기 구현의 예제로 설명해놓은 방식이기도 하고 큰 학습노력 없이 간단하게 구현할 수 있어 많은 사람들이 사용하고 있었습니다.

 

다만, Android 개발자 문서에 따르면... Android 11 부터 AsyncTask 가 Deprecated로 지정되었습니다.

11이전의 Android 버전들을 타게팅하는 앱들이야 상관없겠지만.. 모바일 디바이스는 계속 신모델이 출시되기 마련이고

Android 11이 마이너하지 않게 되는것은 시간문제일 것이므로.... 미리미리 대응방안을 마련해 두어야 합니다.

 

저 또한, AsyncTask를 주로 사용하다가, Deprecated 소식을 접한 뒤 RxJava나 Corutine을 이용하여 비동기를 구현하는 방향으로 기존 앱들과 신규 개발할 앱들을 변경하기로 했습니다.

 

 


먼저 기존에 사용하던 AsyncTask를 알아봅니다.

 

 

1. AsyncTask

 

AsyncTask의 기본 구성은 아래와 같습니다.

onPreExecute, doInBackground, onPostExecute 의 주요부분과 

 

좀 더 자세한 구성은 개발자 문서의 AsyncTask를 참고 해 주세요.

 

class SomethingAsync extends AsyncTask<Void , Integer , Integer> {
        @Override
        protected void onPreExecute() {
            progressBar.setVisibility(View.VISIBLE);
        }
        @Override
        protected Integer doInBackground(Void... voids) {
           

            return value;
        }
        @Override
        protected void onProgressUpdate(Integer ... values) {
            progressbar.setProgress(values[0].intValue());
        }
        
        @Override
        protected void onPostExecute(Integer result) {
         
        }
        
      
    }

AsyncTask<>에는 변수가 3개 들어가는데,

 

AsyncTask<doInBackground()의 변수 종류, onProgressUpdate()의 사용할 변수, onPostExecute()의 변수>

doInBackground()의 변수 : AsyncTask를 execute할 때 전해줄 값
onProgressUpdate()의 변수 : 진행상황을 업데이트할 때 사용 할 값
onPostExecute()의 변수 : AsyncTask가 끝난 뒤 결과 값

넣어서 사용해도 좋고, Void로 처리한뒤, 외부에서 값을 따로 적용하여도 좋습니다.

 

AsyncTask를 통해서 Jsoup를 사용하는 예제는 아래와 같습니다.

public static class GetTitle extends android.os.AsyncTask<Void, Void, String> {

            String title;

            @Override
            public void onPreExecute() {
            	 progressBar.setVisibility(View.VISIBLE);
                super.onPreExecute();

            }

            @Override
            public String doInBackground(Void... params) {
               Document doc = Jsoup.connect(URL).timeout(3000).get();
               Elements elements = doc.select("title");
               title = elements.get(0).text();
 
               return title;
            }

            @Override
            public void onPostExecute(String result) {
             progressBar.setVisibility(View.GONE);
             FragmentSomething.replaceFragment(String);


            }

    }

 

Progressbar가 진행상황을 표시 하지 않으므로, onProgressUpdate()는 사용하지 않았습니다.

 

AsyncTask가 Deprecated되므로 RxJava나 Coroutine으로 변경해 주어야 합니다.


2. RxJava

 

RxJava는 Reactive Java에서 이름을 따왔다고 하는데요,  Reactive Programming을 자바에서 구현하기 위해서 등장한 라이브러리 입니다. Reactive Programming은, 비동기 데이터 흐름을 중시하는 프로그래밍으로 외부에서 자유롭게 데이터 입출력을 하여도 메인쓰레드와 방해하지 않는것을 중시하는 프로그래밍입니다.

 

복잡하니까 그냥 비동기작업을 구현하기 위한 또다른 방법으로 간단하게 생각하도록 합시다.

 

 

RxJava의 사용을 위해서는 App단의 build.gradle에 다음을 implementation 해 주어야 합니다.

    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.15'

 

 

다음은 RxJava를 이용해서 AsyncTask와 유사한 구조로 구성한 예제입니다.

Disposable backgroundtask;

void BackgroundTask(String URLs) {
//onPreExecute

        progressBar.setVisibility(View.VISIBLE);

        backgroundtask = Observable.fromCallable(() -> {
//doInBackground

            Document doc = Jsoup.connect(URLs).timeout(3000).get();
            Elements elements = doc.select("title");
            title = elements.get(0).text();
            Log.d("로그 doInBackground", title);

            return false;
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe((result) -> {

//onPostExecute
               
                    replaceFragment(title);
                    progressBar.setVisibility(View.GONE);
                    
                    backgroundtask.dispose();
                });
    }

 

3. Coroutine

 

제가 Coroutine을 접했던것은 Unity에서 였는데요,

 

게임에서 캐릭터가 미사일을 발사하거나 점프 할때를 가정해보면 이것이 게임의 UI 변경과 전혀 충돌하지 않는 것을 볼 수있습니다. 이렇게 UI에 독립적인 비동기 작업을 Unity에서 구현할 때 Coroutine을 사용합니다.

 

앱의 경우에도 이와 동일한 비동기 구현이라는 점을 통해서 볼때 Coroutine은 비동기 구현에 매우 적합한 도구라는것을 자연스럽게 떠올릴 수 있습니다.

 

Coroutine은 안드로이드 개발자 문서에서 공식적으로 AsyncTask 대신 사용할 것을 권하고 있습니다.

 

안드로이드에서는 Kotlin 1.1 부터 Coroutine을 지원하고 있고 이 때문에 Kotlin으로 이를 구현해야 하고

AsyncTask의 여러 파트를 그냥 사용하면 되는것에 비해 Tread의 이해가 필요하기 때문에 학습노력이 좀 필요합니다.

 

 

Coroutine의 사용을 위해서는 App단의 build.gradle에 다음을 implementation 해 주어야 합니다.

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
    implementation 'com.google.guava:guava:27.0.1-android'

다음은 Coroutine를 이용해서 AsyncTask와 유사한 구조로 구성한 예제입니다.

 

class Coroutine {
    companion object {
    
    
  fun BackgroundTask (progressBar: ProgressBar, transaction : FragmentTransaction, Url : String) {
//onPreExcute
            progressBar.visibility = View.VISIBLE
            
            CoroutineScope(Dispatchers.Main).launch {
//doInBackground
        val Title = async(Dispatchers.Default) {
            val document = Jsoup.connect(Url).timeout(3000).get()
            val elements = document.select("title")
            elements[0].text().toString()

        }.await()
//onPostExecute
                replaceFragment(transaction, Title)
                progressBar.visibility = View.GONE
    }
    
    
    }

}

* Coroutine 은 Kotlin을 통해서 구현할 수 있기 때문에, Java기반 프로젝트 내부에 별도의 Kotlin Class인 Cotoutine을 생성한 뒤 Companion object 내부에 함수를 구현하였습니다.

 

 

 

반응형

댓글