본문 바로가기
Rx

안드로이드의 RxJava 활용 - 8( 스레드를 RxAndroid로 대체하기)

by 봄석 2018. 12. 30.

본 내용은 필자가 학습한 내용을 정리하는 내용입니다.

대부분 의 내용이 아래 책의 내용이므로 원서를 구매해서 직접보시는걸 추천드립니다!

RxJava 프로그래밍 리액티브 프로그래밍 기초부터 RxAndroid까지 한 번에

유동환 , 박정준 지음 | 한빛미디어 | 2017년 09월 04일 출간

http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788968488658&orderClick=LAV&Kc=


저자님의 블로그

https://brunch.co.kr/@yudong#info




안드로이드 스레드를 대체하는 RxAndroid

안드로이드는 기본적으로 싱글 스레드 모델입니다. 그래서 처리하는 데 오래 걸리는 데이터 전송이나 파일 입출력등은 별도의 스레드로 분리하여 작업해야합니다. 이 부분을 고려하지 않고 앱을 개발하면 성능이 나빠지거나 애플리케이션이 응답하지않는 현상(ANR)이 발생하기도합니다.


스레드를 효과적으로 관리하려면 스케줄러를 만들어서 관리해야합니다. 여기에서는 리액티브 프로그래밍에서 안드로이드 스레드를 어떻게 관리하는지 알아보겠습니다.




뷰와 뷰그룹의 스레드 관리

안드로이드의 뷰나 뷰 그룹은 UI스레드에서만 업데이트할 수 있게 설계되어있습니다. 여러스레드에서 동시 UI를 업데이트 할 때 발생할 수 있는 동기화 문제를 예방하기 위함입니다. 그럼 일반 스레드에서 작업한 결과를 어떻게 뷰에 업데이트 할 수 있을까요 ? 안드로이드는 이를위해 Looper와 Handler 클레스를 제공합니다.


UI 스레드와 통신하기

스레드는 Handler를 통해 Message를 Message Queue에 넣습니다. UI 스레드는 Looper 클래스를 이용해 Message Queue에 접근하여 다른 스레드에서 보내는 데이터를 처리할 수 있습니다. 즉 , 개별스레드에서 UI스레드와 통신할 방법은 Handler를 통해 Message를 보내고 UI 스레드는 Looper를 이용해 Message를 꺼내서 사용하는 구조입니다.


여기좀 더 구조를 설명하자면 스레드가 Hadler 객체를 생성하는 것이 아니라 Handler 객체가 스레드를 생성하는 것입니다. 그리고 Handler 객체는 실행되는 스레드와 스레드에 있는 Message Queue에 종속됩니다 . 따라서 멀티 스레드를 이용한 병렬 처리 같은 기능은 위 그림같은 동작원리를 완벽하게 이해해야 합니다. 


참고로 안드로이드 스레드 관리를 위해 Handler 클래스를 좀 더 사용하기 쉽게 래핑한 클래스 HandlerThread 클래스, 다음부터 RxAndroid를 도입할 AsyncTask 클래스도 제공합니다.




AsyncTask 클래스에 RxAndroid 적용하기 

AsyncTask는 안드로이드에서 제공하는 추상 클래스로 안드로이드에서 사용하는 대표적인 스레드 중 하나입니다. 별도의 Handler 클래스나 스레도 사용 없이 UI 스레드에서 백그라운드 작업을 수행하고 결과를 바로 뷰 화면에 업데이트 할 수 있습니다.


AsyncTask 클래스 활용 예

private void initAndroidAsync(){
        myAsyncTask=new MyAsyncTask();
        myAsyncTask.execute("Hello","async","world");
    }
 
    public class MyAsyncTask extends AsyncTask<String,Void,String> {//param progress result
 
        @Override
        protected String doInBackground(String... params) {
            StringBuilder word=new StringBuilder();
            for(String s:params){
                word.append(s).append(" ");
            }
            return word.toString();
        }
 
        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            mAndroidTextView.setText(result);
        }
    }




AsyncTaskActivity 클래스의 initAndroidAsync() 메소드에서는 AsyncTask<String,Void,String> 을 상속한 MyAsyncTask 클래스의  doInBackgroud() 를 호출하여 "hello", "async" "world"를 전달합니다.


AsynckTask 클래스는 UI 스레드가 아닌 싱글 워크 스레드에서 동작하며 publishProgress() 를 이용하여 실행 중간중간에 바로 화면을 갱신한다는 장점이 있습니다. 


앞서 안드로이드는 화면에 업데이트 하기 위해  UI 스레드를 이용한다고 했습니다. AsyncTask 클래스는 위에서 설명한 스레드나 루프의 원리를 이해하지 못하더라도 백그라운드에서 처리한 결과를 바로 UI 스레드에 업데이트 할 수 있는 편리한 기능을 제공합니다. 또한 

THREAD_POOL_EXECUTOR를 이용하여 병렬로 처리할 수 있다는 장점도 있습니다.


하지만 아래와 같은 단점도 있으므로 사용할 떄 주의해야 합니다

1) 오직 한번만 실행된다 . 재사용이 불가능하다

2) 액티비티 종료를 명시해야만 종료되므로 메모리 누수가 발생한다.

3) AsyncTask 클래스는 항상 UI 스레드 위에서 불러와야 한다.



그렇담 RxAndroid를 이용하여 안드로이드 스레드를 어떻게 대처하는지 보도록하겠습니다

RxAndroid를 이용한 스케줄러

private void initRxAsync(){
        Observable.just("Hello","rx","world")
                .reduce((x,y)->x+" "+y)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(getObserver());
                /*.subscribe(mRxTextView::setText,
                        e-> Log.e(TAG,e.getMessage()),
                        ()->Log.i(TAG,"done")
                        );*/
 
    }
 
    private MaybeObserver<String> getObserver(){
        return new MaybeObserver<String>(){
 
            @Override
            public void onSubscribe(Disposable d) {
 
            }
 
            @Override
            public void onSuccess(String s) {
                mRxTextView.setText(s+" useMaybe");
            }
 
            @Override
            public void onError(Throwable e) {
                Log.e(TAG,e.getMessage());
            }
 
            @Override
            public void onComplete() {
                Log.i(TAG,"done");
            }
        };
    }



Observable 생성자중 just() 함수를 이용하여 "hello","rx", "world"라는 데이터를 발행합니다

reduce() 함수는 StringBuilder클래스의 append() 메소드의 역할을 합니다.

다음으로는 observeOn(AndroidSchedulers.mainThread()) 를 이용하여 구독자가 실행될 스레드를 안드로이드의 UI 스레드로 지정합니다. 에러없이 정상적으로 텍스트뷰에 'hello rx world' 가 출력됩니다. 이처럼 간단한 설정만으로도 구독자나 Observable로 데이터를 발행할 스레드를 설정할 수 있습니다.


주석처리된 코드는 getObserver() 메소드를 만들지않고 람다표현식을 이용하는 방법과 구독자를 직접 구현하는 방법을 모두 적용했습니다. 중요한 것은 코드 실행중 발생하는 모든 에러는 

구독자의 onError() 함수에서 처리할 수 있어야 한다는 점입니다. 람다 표현식으로 코드를 간소화 하는것도 좋지만 가능하면 모든 메소드를 구현하여 상황에 맞게 개발자가 직접 처리해 주는 방식을 권합니다.


이처럼 RxAndroid는 강력한 스케줄러 기능이 있습니다.이제 어렵지않게 파일 다운로드나 입 출력 처리 등 시간이 오래걸리는 작업을 간단히 대체할 수 있습니다. 아래 표에서는 간단히 RxAndroid의 함수와 AsyncTask 클래스의 기능을 비교했습니다. 정확히 1:1로 비교할 수는 없지만 비슷한 기능이 있는 메소드와 함수입니다 .


AsyncTask 

RxAndroid 

 excute() 

 subscribe() 

 doInBackground()

 리액티브 연산자와 함께 사용하는 onSubscribe() 

 onPostExecuted() 

 observer 



댓글