본문 바로가기
Rx

안드로이드의 RxJava 활용 - 9( TimerTask를 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



TimerTask를 대체하는 RxAndroid

안드로이드에서는 주기적으로 실행하는 동작을 구현하는 여러 가지 방법이 있지만 보통 Timer 클래스나 Handler 클래스를 이용합니다. Timer 클래스는 shedule() 메소드를 이용하여 지연시간을 설정하거나, 특정 시간에 어떤 동작을 실행할 수도 있으며, 고정된 시간을 설정해 동작을 반복 실행할 수 있습니다. Handler 클래스는 postDelyed() 메소드를 이용하여 지연 시간 설정이나 반복 실행을 구현할 수 있습니다. 먼저 Timer 클래스를 어떻게 활용하는지 살펴보겠습니다.


Timer클래스 활용하기

private int DELAY = 0;
    private int PERIOD = 1000;
    private int count;
 
    private Timer mTimer;
 
    public void timerStart() {
        count = 0;
        mTimer = new Timer();
        mTimer.scheduleAtFixedRate(new TimerTask() {
 
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
 
                    @Override
                    public void run() {
                        mTextView.setText(String.valueOf(count++));
                    }
                });
 
            }
        }, DELAY, PERIOD);
 
    }
    public void timerStop(){
        if(mTimer!=null){
            mTimer.cancel();
        }

    }



Timer 클래스를 이용하여 1초마다 한번씩 run() 메소드를 호출하는 코드입니다

 DELAY는 첫 실행시 지연시간을 선언하는 부분입니다. 1000ms 를 설정하면 호출한지 1분 후부터 1초에 한번씩 실행합니다.



다음은 Timer 클래스에서 사용할 수 있는 메소드를 소개한 것입니다.

메소드이름 

 설명 

 schedule(TimerTask task, long delay, long period)

 설정된 지연 시간 후 주기적으로 실행됩니다. 

 schedule(TimerTask task, Date time) 

 지정된 시간에 한번 실행합니다. 

 shcedule(TimerTask task, Date firstTime, long period) 

 지정된 시간에 시작한 후 주기적으로 실행됩니다. 

 schedule(TimerTask , long delay)  

 설정된 지연 시간 후 고정된 간격으로 주기적으로 실행합니다. 

 scheduleAtFixedRate(TimerTask task, long delay, long period) 

 설정된 지연 시간 후 고정된 간격으로 주기적으로 실행합니다.

 schedule() 메소드와 다른 점은 정확히 고정된 시간에 실행된다는 점입니다. 

 shceduleAtFixedRate(TimerTask task, Date firstTime, long period) 

 지정된 시간에 실행을 시작한 후 고정된 간격으로 주기적으로 실행합니다. 


메소드를 선택할 때 주의해야 하는 부분이 있습니다. 메소드 이름에 'Fixed' 라는 단억 ㅏ없다면 Timer 클래스 실행 주기에 오차가 발생합니다. 예를 들어 정확히 30초 주기로 ping을 보내야 하는 시스템이라면 schedule() 메소드 보다는 schedulerAtFixedRate() 메소드를 사용해야 합니다.

 scheduler() 메소드는 약 30초마다 실행 주기에 오차가 발생할 수 있습니다. 반면에 schedulerAtFixedRate() 메소드는 정확하게 30초 마다 한번씩 호출됩니다.


실행횟수를 제한해야 할때도 필요할 것입니다. 이럴 때는 CountDownTimer 클래스를 사용하는것이 올바른 선택입니다.


CountDownTimer 클래스의 활용 예

 private static final int MILLISINFUTURE= 11*1000;
 private static final int COUNT_DOWN_INTERVAL=1000;
 
 CountDownTimer mCountDownTimer;
 
 public void initCountDownTask(){
        mCountDownTimer=new CountDownTimer(MILLISINFUTURE,COUNT_DOWN_INTERVAL) {
            @Override
            public void onTick(long millisUntilFinished) {
                mTextView.setText(String.valueOf(count--));
            }
 
            @Override
            public void onFinish() {
                mTextView.setText("Finish .");
            }
        };
    }
    public void countDownTimerStart() {
        count = 10;
        mCountDownTimer.start();
    }



11초동안 1초에 한번씩 onTick() 메소드를 호출합니다. 완료할 대는 onFinish() 메소드가 호출됩니다.




Handler 클래스를 이용하여 Timer 클래스를 대체할 수도 있습니다.

Handler클래스 활용 예

private Handler mHandler;
    private Runnable timer=new Runnable() {
        @Override
        public void run() {
            mTextView.setText(String.valueOf(count++));
            mHandler.postDelayed(this,1000);
        }
    };
 
    private void initHandler(){
        mHandler=new Handler();
    }
    public void handlerStart(){
        mHandler.postDelayed(timer,0);
    }



Handler클래스로 Timer 클래스를 대체하는 실제동작은 Runnable 객체 안 1초에 한번씩 Handler를 호출하는 부분 (mHandler.postDelayed(this,1000))에서 이루어집니다. 처음 실행할 때 지연시간을 설정하고 싶은 결우에는 handlerStart() 메소드에서 지연시간을 설정하면 됩니다.



이제 RxAndroid를 이용하여 Timer 클래스를 대체해 보겠습니다. 두 가지 방법으로 구현해 보았습니다. 첫 번째는 RxAndroid에서 Timer 클래스를 대체하는 방법중 가장 범용적으로 사용하는 interval() 함수입니다.  참고로 RxJava에서는 여섯가지 종류의 interval() 함수를 제공하며, 0부터 시작하여 Long.MAX_VALUE 까지 반복합니다.

public class PollingFragment extends Fragment {
    private static final long INITIAL_DELAY=0L;
    private static final long PERIOD=3L;
 
    @BindView(R.id.lv_polling_log)
    ListView mLogView;
    Unbinder mUnbinder;
    // Log
    private LogAdapter mLogAdapter;
    private List<String> mLogs;
 
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View layout=inflater.inflate(R.layout.fragment_polling,container,false);
        mUnbinder=ButterKnife.bind(this,layout);
        setupLogger();
        return layout;
    }
 
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if(mUnbinder!=null){
            mUnbinder.unbind();
        }
    }
    @OnClick(R.id.btn_polling)
    void polling(){startPollingV1();}
 
 
    private void startPollingV1(){
        Observable<String> ob=Observable.interval(INITIAL_DELAY,PERIOD,TimeUnit.SECONDS)
                .flatMap(o->Observable.just("polling #1 "+ o.toString()));
 
        ob.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this::log);
 
    }
    //Log 
}




위 코드의 동작흐름은 아래와 같습니다

1) interval() 함수를 이용하여 3초마다 정수를 발생하는 Observable을 생성합니다.

2) 생성한 Observable은 flatMap() 함수에서 'polling #1 과 발행한 정수를 결합한 새로운 Observable로 변경합니다.

3) subscribeOn()과 observeOn() 함수를 이용하여 실행할 스레드를 설정합니다. 결과를 화면에 바로 업데이트 해야하므로 결과를 표시하는 스케줄러는 mainThread로 설정합니다.


요약하면 작업스레드와 UI 스레드를 분리하여 3초마다 화면을 업데이트 하는 기능을 구현한 것입니다.


다음은 위에서 본 코드를 repeatWhen()과 delay() 함수를 활용하도록 변경한 내용입니다,

 private void startPollingV2(){
        Observable<String> ob2=Observable.just("polling #2 ")
                .repeatWhen(o->o.delay(3,TimeUnit.SECONDS));
        ob2.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this::log);
    }



위 코드의 동작흐름은 다음과 같습니다

1) just() 함수를 이용하여 문자를 발행하는 Observable을 생성합니다.

2) repeatWhen() 함수를 이용하여 동일한 Observable 객체를 계속 발행하게 설정합니다.

3) repeatWhen() 함수는 입력된 Observable에 delay() 함수를 이용하여 3초의 지연을 설정합니다.

4) 역시 subscribeOn() 과 observeOn() 함수를 이용하여 실행할 스레드를 지정합니다.

결과를 화면에 바로 업데이트해야하므로 결과를 표시하는 스케줄러는 mainThread로 지정합니다.


즉 3초의 지연 시간 설정은 delay() 함수가 반복 실행은 repeatWhen() 함수가 담당하는 구조입니다. 


마지막으로 위에 코드들에서 이용한 AndroidSchedulers 클래스를 간단히 설명하겠습니다.

RxAndroid에서 추가되는 스케줄러 함수는 두 가지로 하나는 mainThread(), 다른하나는 from() 입니다. mainThread 함수는 스케줄러 내부에서 직접 MainLooper에 바인딩하며, from() 함수는 개발자가 임의의 Looper객체를 설정할 수 있습니다. 즉 , AndroidSchedulers.mainThread() 는 

AndroidSchedulers.from(Looper.getMainLooper())과 동일합니다. 

스레드 사이에서 통신하기 위해서는 Handler 클래스가 필요합니다. 기본적으로 RxAndroid 에서도 Handler 클래스를 이용하여 스레드와 통신하는 구조이며, UI 스레드와 통신을 위해서 MainLooper를 이용하여 스레드안에서 핸들러를 생성합니다.



-----------------------------------------------------------------------------------------------------

repeatWhen() 함수의 람다식을 이해하는 방법

RxJava는 함수형 프로그래밍과 연관이 있습니다. 예를들어 아래의 repeatWhen() 함수를 살펴보면 인자로 함수형 인터페이스를 활용합니다. 즉 <? super Observable<java.lang.Object> 타입의 Observable을 입력받아 <? extends ObservableSource<?>>으로 변경하여 리턴하게 됩니다.


repeatWhen() 함수의 원형

1
2
repeatWhen(Function<? Super Observable<java.lang.Object>,
    ? extneds ObservableSource<?>> handler>
cs



자바표현식과 람다표현식의 비교

자바 표현식 

람다 표현식 

 Supplier() 

 ()->T 

 Consumer(T) 

 T-> () 

 Predicate(T) 

 T-> Boolean 

 Function(T, R) 

 T-> R 

 BiFunction(T, U, R) 

 (T, U)->  


댓글