언제 RxJava Observable을 사용해야하고 언제 Android에서 간단한 콜백을 사용해야합니까?
내 앱의 네트워킹을 위해 노력하고 있습니다. 그래서 Square 's Retrofit 을 사용해보기로 결정했습니다 . 나는 그들이 간단한 지원을 참조하십시오Callback
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
그리고 RxJava Observable
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
둘 다 언뜻 보면 비슷해 보이지만 구현이되면 흥미로워집니다.
간단한 콜백 구현은 다음과 유사합니다.
api.getUserPhoto(photoId, new Callback<Photo>() {
@Override
public void onSuccess() {
}
});
아주 간단하고 간단합니다. 그리고 Observable
그것으로 빨리 장황하고 상당히 복잡해집니다.
public Observable<Photo> getUserPhoto(final int photoId) {
return Observable.create(new Observable.OnSubscribeFunc<Photo>() {
@Override
public Subscription onSubscribe(Observer<? super Photo> observer) {
try {
observer.onNext(api.getUserPhoto(photoId));
observer.onCompleted();
} catch (Exception e) {
observer.onError(e);
}
return Subscriptions.empty();
}
}).subscribeOn(Schedulers.threadPoolForIO());
}
그리고 그것은 아닙니다. 여전히 다음과 같은 작업을 수행해야합니다.
Observable.from(photoIdArray)
.mapMany(new Func1<String, Observable<Photo>>() {
@Override
public Observable<Photo> call(Integer s) {
return getUserPhoto(s);
}
})
.subscribeOn(Schedulers.threadPoolForIO())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Photo>() {
@Override
public void call(Photo photo) {
//save photo?
}
});
여기에 뭔가 빠졌습니까? 아니면 Observable
s 를 사용하는 것이 잘못된 경우 입니까? Observable
간단한 콜백보다 언제 / 선호해야 합니까?
최신 정보
@Niels가 그의 답변이나 Jake Wharton의 예제 프로젝트 U2020 에서 보여 주듯이 개장을 사용하는 것은 위의 예보다 훨씬 간단 합니다. 그러나 본질적으로 문제는 동일하게 유지됩니다. 한 가지 방법을 사용해야합니까?
간단한 네트워킹 작업의 경우 콜백에 비해 RxJava의 장점은 매우 제한적입니다. 간단한 getUserPhoto 예제 :
RxJava :
api.getUserPhoto(photoId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Photo>() {
@Override
public void call(Photo photo) {
// do some stuff with your photo
}
});
콜백 :
api.getUserPhoto(photoId, new Callback<Photo>() {
@Override
public void onSuccess(Photo photo, Response response) {
}
});
RxJava 변형은 콜백 변형보다 훨씬 좋지 않습니다. 지금은 오류 처리를 무시하겠습니다. 사진 목록을 보자.
RxJava :
api.getUserPhotos(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<List<Photo>, Observable<Photo>>() {
@Override
public Observable<Photo> call(List<Photo> photos) {
return Observable.from(photos);
}
})
.filter(new Func1<Photo, Boolean>() {
@Override
public Boolean call(Photo photo) {
return photo.isPNG();
}
})
.subscribe(
new Action1<Photo>() {
@Override
public void call(Photo photo) {
list.add(photo)
}
});
콜백 :
api.getUserPhotos(userId, new Callback<List<Photo>>() {
@Override
public void onSuccess(List<Photo> photos, Response response) {
List<Photo> filteredPhotos = new ArrayList<Photo>();
for(Photo photo: photos) {
if(photo.isPNG()) {
filteredList.add(photo);
}
}
}
});
이제 RxJava 변형은 더 작지 않지만 Lambdas에서는 콜백 변형에 더 가까워집니다. 또한 JSON 피드에 액세스 할 수 있으면 PNG 만 표시 할 때 모든 사진을 검색하는 것이 이상합니다. 피드 만 조정하면 PNG 만 표시됩니다.
첫 결론
올바른 형식으로 준비한 간단한 JSON을로드 할 때 코드베이스가 더 작아지지 않습니다.
이제 좀 더 재미있게 만들어 봅시다. userPhoto를 검색하고 싶지만 Instagram 복제본이 있고 두 개의 JSON을 검색하려고한다고 가정 해 봅시다. 1. getUserDetails () 2. getUserPhotos ()
이 두 JSON을 병렬로로드하려고하며 둘 다로드되면 페이지가 표시되어야합니다. 콜백 변형은 조금 더 어려워집니다. 콜백을 2 개 생성하고 활동에 데이터를 저장하고 모든 데이터가로드 된 경우 페이지를 표시해야합니다.
콜백 :
api.getUserDetails(userId, new Callback<UserDetails>() {
@Override
public void onSuccess(UserDetails details, Response response) {
this.details = details;
if(this.photos != null) {
displayPage();
}
}
});
api.getUserPhotos(userId, new Callback<List<Photo>>() {
@Override
public void onSuccess(List<Photo> photos, Response response) {
this.photos = photos;
if(this.details != null) {
displayPage();
}
}
});
RxJava :
private class Combined {
UserDetails details;
List<Photo> photos;
}
Observable.zip(api.getUserDetails(userId), api.getUserPhotos(userId), new Func2<UserDetails, List<Photo>, Combined>() {
@Override
public Combined call(UserDetails details, List<Photo> photos) {
Combined r = new Combined();
r.details = details;
r.photos = photos;
return r;
}
}).subscribe(new Action1<Combined>() {
@Override
public void call(Combined combined) {
}
});
우리는 어딘가에 도착하고 있습니다! RxJava의 코드는 이제 콜백 옵션만큼 큽니다. RxJava 코드가 더 강력합니다. 최신 비디오와 같이 세 번째 JSON을로드해야하는 경우 어떻게 될지 생각하십니까? RxJava는 약간만 조정하면되며 콜백 변형은 여러 위치에서 조정해야합니다 (각 콜백마다 모든 데이터가 검색되는지 확인해야 함).
또 다른 예; Retrofit을 사용하여 데이터를로드하는 자동 완성 필드를 만들고 싶습니다. EditText에 TextChangedEvent가있을 때마다 웹콜을하고 싶지 않습니다. 빠르게 입력 할 때는 마지막 요소 만 호출을 트리거해야합니다. RxJava에서는 debounce 연산자를 사용할 수 있습니다.
inputObservable.debounce(1, TimeUnit.SECONDS).subscribe(new Action1<String>() {
@Override
public void call(String s) {
// use Retrofit to create autocompletedata
}
});
콜백 변형을 만들지는 않지만 이것이 훨씬 더 많은 일이라는 것을 이해할 것입니다.
결론 : RxJava는 데이터가 스트림으로 전송 될 때 매우 좋습니다. Retrofit Observable은 스트림의 모든 요소를 동시에 푸시합니다. 콜백에 비해 그 자체로는 특히 유용하지 않습니다. 그러나 스트림에 여러 요소가 푸시되고 다른 시간이 걸리고 타이밍 관련 작업을 수행해야하는 경우 RxJava는 코드를 유지 관리하기가 훨씬 쉽습니다.
Observable은 이미 Retrofit에서 수행되었으므로 코드는 다음과 같습니다.
api.getUserPhoto(photoId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Photo>() {
@Override
public void call(Photo photo) {
//save photo?
}
});
getUserPhoto ()의 경우 RxJava의 장점은 크지 않습니다. 그러나 사용자의 모든 사진을 가져올 때 이미지가 PNG이고 서버 측에서 필터링을 수행하기 위해 JSON에 액세스 할 수없는 경우에만 다른 예를 들어 보겠습니다.
api.getUserPhotos(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<List<Photo>, Observable<Photo>>() {
@Override
public Observable<Photo> call(List<Photo> photos) {
return Observable.from(photos);
}
})
.filter(new Func1<Photo, Boolean>() {
@Override
public Boolean call(Photo photo) {
return photo.isPNG();
}
})
.subscribe(
new Action1<Photo>() {
@Override
public void call(Photo photo) {
// on main thread; callback for each photo, add them to a list or something.
list.add(photo)
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// on main thread; something went wrong
System.out.println("Error! " + throwable);
}
},
new Action0() {
@Override
public void call() {
// on main thread; all photo's loaded, time to show the list or something.
}
});
이제 JSON은 사진 목록을 반환합니다. 우리는 그것들을 개별 아이템에 flatMap 할 것입니다. 이렇게하면 필터 방법을 사용하여 PNG가 아닌 사진을 무시할 수 있습니다. 그런 다음 구독을하고 모든 행이 완료되면 각 개별 사진에 대한 콜백, errorHandler 및 콜백을받습니다.
TLDR 여기에 포인트; 콜백은 성공 및 실패에 대한 콜백 만 반환합니다. RxJava Observable을 사용하면 맵, 축소, 필터링 및 기타 다양한 작업을 수행 할 수 있습니다.
rxjava를 사용하면 적은 코드로 더 많은 일을 할 수 있습니다.
앱에서 빠른 검색을 구현한다고 가정 해 봅시다. 콜백을 사용하면 이전 요청을 구독 취소하고 새로운 요청을 구독하고 방향 변경을 직접 처리하는 것에 대해 걱정했습니다. 코드가 너무 많고 너무 장황하다고 생각합니다.
rxjava를 사용하면 매우 간단합니다.
public class PhotoModel{
BehaviorSubject<Observable<Photo>> subject = BehaviorSubject.create(...);
public void setUserId(String id){
subject.onNext(Api.getUserPhoto(photoId));
}
public Observable<Photo> subscribeToPhoto(){
return Observable.switchOnNext(subject);
}
}
빠른 검색을 구현하려면 TextChangeListener를 수신하고 호출하기 만하면됩니다. photoModel.setUserId(EditText.getText());
조각 또는 활동의 onCreate 메소드에서 photoModel.subscribeToPhoto ()를 반환하는 Observable에 가입하면 항상 최신 Observable (요청)에서 방출 된 항목을 방출하는 Observable을 반환합니다.
AndroidObservable.bindFragment(this, photoModel.subscribeToPhoto())
.subscribe(new Action1<Photo>(Photo photo){
//Here you always receive the response of the latest query to the server.
});
예를 들어 PhotoModel이 싱글 톤 인 경우 BehaviorSubject는 구독시기에 관계없이 마지막 서버 응답을 생성하므로 방향 변경에 대해 걱정할 필요가 없습니다.
이 코드 줄을 사용하여 즉시 검색을 수행하고 방향 변경을 처리했습니다. 적은 코드로 콜백으로 이것을 구현할 수 있다고 생각하십니까? 나는 그것을 의심한다.
우리는 일반적으로 다음과 같은 논리를 따릅니다.
- 간단한 단일 응답 전화 인 경우 콜백 또는 미래가 더 좋습니다.
- 여러 응답 (스트림)을 가진 호출이거나 다른 호출간에 복잡한 상호 작용이있는 경우 (@Niels ' answer 참조 ) Observables가 더 좋습니다.
다른 답변의 샘플과 결론에 따르면 간단한 한두 단계 작업에는 큰 차이가 없다고 생각합니다. 그러나 콜백은 간단하고 간단합니다. RxJava는 더 복잡하고 간단한 작업을하기에는 너무 큽니다. AbacusUtil 의 세 번째 해결책이 있습니다 . 콜백, RxJava, CompletableFuture (AbacusUtil)와 : 나에게 세 가지 솔루션 위의 사용 사례 구현하자 Retrolambda를 :
네트워크에서 사진을 가져오고 장치에 저장 / 표시 :
// By Callback
api.getUserPhoto(userId, new Callback<Photo>() {
@Override
public void onResponse(Call<Photo> call, Response<Photo> response) {
save(response.body()); // or update view on UI thread.
}
@Override
public void onFailure(Call<Photo> call, Throwable t) {
// show error message on UI or do something else.
}
});
// By RxJava
api.getUserPhoto2(userId) //
.observeOn(AndroidSchedulers.mainThread())
.subscribe(photo -> {
save(photo); // or update view on UI thread.
}, error -> {
// show error message on UI or do something else.
});
// By Thread pool executor and CompletableFuture.
TPExecutor.execute(() -> api.getUserPhoto(userId))
.thenRunOnUI((photo, error) -> {
if (error != null) {
// show error message on UI or do something else.
} else {
save(photo); // or update view on UI thread.
}
});
사용자 정보와 사진을 동시에로드
// By Callback
// ignored because it's little complicated
// By RxJava
Observable.zip(api.getUserDetails2(userId), api.getUserPhoto2(userId), (details, photo) -> Pair.of(details, photo))
.subscribe(p -> {
// Do your task.
});
// By Thread pool executor and CompletableFuture.
TPExecutor.execute(() -> api.getUserDetails(userId))
.runOnUIAfterBoth(TPExecutor.execute(() -> api.getUserPhoto(userId)), p -> {
// Do your task
});
휠을 재발 명하는 것처럼 보이며 이미 수행중인 작업은 이미 개조되어 구현되었습니다.
예를 들어 retrofit의 RestAdapterTest.java를 살펴보면 , Observable이 있는 인터페이스 를 리턴 유형으로 정의한 다음이를 사용할 수 있습니다.
필자는 개인적으로 Rx를 사용하여 데이터에서 필터링, 맵 또는 이와 유사한 작업을 수행해야하거나 이전 호출 응답을 기반으로 다른 API 호출을 수행 해야하는 경우 API 응답을 얻는 것을 선호합니다.
'IT story' 카테고리의 다른 글
ASP.NET MVC에서 favicon.ico 제공 (0) | 2020.04.04 |
---|---|
왜 isset ()과! empty ()를 모두 확인 (0) | 2020.04.04 |
awk 스크립트에서 쉘 변수를 어떻게 사용합니까? (0) | 2020.04.04 |
액션 크리에이터에서 Redux 상태에 액세스하고 있습니까? (0) | 2020.04.04 |
JavaScript 명명 규칙 (0) | 2020.04.04 |