개발하는 두더지

[Rxjava2] Rxjava2 를 이용한 로또 번호 당첨 결과 확인하기 본문

Java,Android

[Rxjava2] Rxjava2 를 이용한 로또 번호 당첨 결과 확인하기

덜지 2018. 6. 14. 19:20

이번주 로또 번호를 가져와서 랜덤으로 생성한 번호와 비교하여 당첨결과를 확인하는 예제를 만들어보겠습니다.


결과물을 먼저 보자면 아래와 같습니다.



목차

0. 환경설정

1. 네트워크에서 로또 당첨 번호 가져오기

2. 로또 랜덤 번호 생성하기

3. 회차 당첨 번호와 랜덤 번호 비교하기

4. 랜덤 번호 당첨 내역을 TextView에 출력하기





0. 환경설정

build.gradle

Rxjava2 + RxAndroid + RxRetrofit 를 이용하므로 아래 라이브러리를 추가합니다

dependencies {
...
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.14'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
}



1. 네트워크에서 로또 당첨 번호 가져오기

Retrofit2 + Rxjava2 를 이용하여 데이터를 가져오겠습니다.

데이터를 가져와서 Deserialize 할 Lotto class를 생성합니다

public class Lotto {
public String bnusNo;
public String drwtNo1;
public String drwtNo2;
public String drwtNo3;
public String drwtNo4;
public String drwtNo5;
public String drwtNo6;

public String getNumber() {
return drwtNo1 + " " + drwtNo2 + " " + drwtNo3 + " " + drwtNo4 + " " + drwtNo5 + " " + drwtNo6;
}

public String getNumberWithBonus() {
return drwtNo1 + " " + drwtNo2 + " " + drwtNo3 + " " + drwtNo4 + " " + drwtNo5 + " " + drwtNo6 + " " + bnusNo;
}
}


Retrofit2 Service interface를 생성합니다

public interface LottoService {
@GET("common.do")
Observable<Lotto> getObLottoRoundInfo(@Query("method") String method,
@Query("drwNo") int drwNo);
}


Retrofit2 adapter class를 생성합니다

public class RestfulAdapter {
private static final String URL = "http://www.nlotto.co.kr/";

public LottoService getRestfulApi() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(LottoService.class);
}

private static class Singleton {
private static final RestfulAdapter instance = new RestfulAdapter();
}

public static RestfulAdapter getInstance() {
return Singleton.instance;
}
}


회차 로또 당첨 번호를 가져옵니다

Schedulers.io() 는 계산 스케줄러와 다르게 네트워크상의 요청을 처리하거나 각종 I/O 작업을 실행하기 위한 스케줄러입니다.

AndroidSchedulers.mainThread() 는 UI 작업을 위한 안드로이드 메인 스레드입니다.

즉,

subscribeOn은 데이터 스트림 생성을 네트워크 스레드에서 하겠다는 것이고, 

observeOn은 데이터 발행을 안드로이드 메인 스레드에서 하겠다는 것을 의미합니다.


알고있어야 할 스케줄러의 특징은

- subscribeOn 만 설정할 경우 모든 흐름이 동일한 스레드에서 실행됩니다.

- 스케줄러를 별도로 지정하지 않으면 현재 스레드에서 동작합니다.

- subscribeOn 은 처음 지정한 스레드를 고정시키므로 다시 subscribeOn을 호출해도 무시합니다.

- observeOn 은 여러번 호출할 수 있으며 호출 이후 그 다음부터 스레드를 바꿀 수 있습니다.


share() 는 subscribe 마다 네트워크 작업이 실행되는데 데이터 생성시 한번만 호출할 수 있도록 해줍니다.


LottoService service = RestfulAdapter.getInstance().getRestfulApi();
Observable<Lotto> resultNumber =
service.getObLottoRoundInfo("getLottoNumber", ROUND)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.share();



2. 로또 랜덤 번호 생성하기

range() 는 for( int i = 0; i < 5; i++) 와 같은 역할을 루프문입니다.

즉, 5개의 랜덤번호를 만드는 스트림입니다.

// 랜덤 번호 생성
Observable<Lotto> randomNumbers = Observable.range(0, 5).map(notUsed -> makeLottoNumber());
private Lotto makeLottoNumber() {
Lotto lotto = new Lotto();

ArrayList<Integer> genaratedNumbers = shuffleNumber();
ascendingSort(genaratedNumbers);

lotto.drwtNo1 = String.valueOf(genaratedNumbers.get(0));
lotto.drwtNo2 = String.valueOf(genaratedNumbers.get(1));
lotto.drwtNo3 = String.valueOf(genaratedNumbers.get(2));
lotto.drwtNo4 = String.valueOf(genaratedNumbers.get(3));
lotto.drwtNo5 = String.valueOf(genaratedNumbers.get(4));
lotto.drwtNo6 = String.valueOf(genaratedNumbers.get(5));

return lotto;
}

private ArrayList<Integer> shuffleNumber() {
ArrayList<Integer> genaratedNumbers = new ArrayList<>();
Collections.shuffle(randomNumbers);
for(int i = 0; i < 6; i++) {
genaratedNumbers.add(randomNumbers.get(i));
}
return genaratedNumbers;
}

private void ascendingSort(ArrayList<Integer> list) {
Collections.sort(list, (o1, o2) -> o1.compareTo(o2));
}




3. 회차 당첨 번호와 랜덤 번호 비교하기

// 회차 번호와 랜덤 번호 비교
Observable<Pair<Lotto, String>> resultSource =
resultNumber.concatMap(result -> randomNumbers.map(random -> checkLottoResult(result, random)));
// ------------------------------------------json---------|
// ------1---2---3---4---5--------------------------------|
// output : (json, 5)

// --json-------------------------------------------------|
// ------------1---2---3---4---5--------------------------|
// output : (json, 1) (json, 2) (json, 3) (json, 4) (json, 5)

zip() 과 combineLatest() 를 사용했을 때 네트워크에서 데이터를 가져오는 속도보다 랜덤번호를 생성하는 속도가 빨라서

1개의 결과물만 비교하게되는 문제가 발생하여 concatMap을 이용하여 네트워크에서 데이터를 먼저 가져온 뒤 랜덤번호를 생성하도록 구현했습니다.


private Pair<Lotto, String> checkLottoResult(Lotto result, Lotto random) {
boolean bonus = false;
String[] results = { result.drwtNo1, result.drwtNo2, result.drwtNo3,
result.drwtNo4, result.drwtNo5, result.drwtNo6, result.bnusNo};

String[] randoms = { random.drwtNo1, random.drwtNo2, random.drwtNo3,
random.drwtNo4, random.drwtNo5, random.drwtNo6};

int count = 0;
for(String r : randoms) {
if(r.equals(results[6])) bonus = true;
if(Arrays.asList(results).contains(r)) count++;
}

String winResult = "";
if(bonus && count == 5) winResult = "2등당첨";
switch(count) {
case 6: winResult = "1등당첨"; break;
case 5: winResult = "3등당첨"; break;
case 4: winResult = "4등당첨"; break;
case 3: winResult = "5등당첨"; break;
case 0:
case 1:
case 2: winResult = "꽝"; break;
}

return Pair.create(random, winResult);
}



4. 랜덤 번호 당첨 내역을 TextView에 출력하기

zip() 은 Observable 2개 이상을 결합해주는 메서드입니다.

// Pair<Pair<Lotto, Sting>, TextView>
TextView[] lists = {lottery1, lottery2, lottery3, lottery4, lottery5};
Disposable disposable2 = Observable.zip(resultSource, Observable.fromArray(lists),
(result, tv) -> Pair.create(result, tv))
.doOnNext(notUsed -> Log.d("MainActivity",
Thread.currentThread().getName() + " | MainActivity.check"))
.subscribe(f -> f.second.setText(f.first.first.getNumber() + "\t\t당첨 결과 : " + f.first.second));



전체소스보기

Comments