Объединить предыдущую наблюдаемую

Я пытаюсь объединить две формы вставки в одну, используя RxJava, RxAndroid и Mosby3, но я не могу найти способ заставить его работать.

Моя структура:

public final class CheckinIntent {

    private final CheckinCommand checkinCommand;
    private final Bitmap signature;

    public CheckinIntent(CheckinCommand checkinCommand, Bitmap signature) {
        this.checkinCommand = checkinCommand;
        this.signature = signature;
    }

    public CheckinCommand getCheckinCommand() {
        return checkinCommand;
    }

    public Bitmap getSignature() {
        return signature;
    }
}

Где я запускаю свое намерение (образец MVI):

final Observable<Bitmap> signatureObservable = Observable.just(BitmapFactory.decodeFile(storage.getFile("signs", booking.getBookingId()).getAbsolutePath()));
        final Observable<CheckinCommand> checkinCommandObservable = Observable.just(new CheckinCommand(booking.getBookingId(), booking.getUserId(), booking.getPartnerId(), userDetailsTextView.getText().toString(), "google.com"));

        final Observable<CheckinIntent> intentObservable = Observable.zip(signatureObservable, checkinCommandObservable, (image, command) -> new CheckinIntent(command, image));

        return saveButtonClickObservable
                .flatMap(bla -> intentObservable);

И связывая все это вместе:

 @Override
    protected void bindIntents() {
        Observable<CheckinViewState> checkinViewStateObservable =
                intent(CheckinView::sendCheckin)
                        .flatMap(checkinIntent -> imageRepository.uploadImage(checkinIntent.getSignature())
                        .flatMap(command ->  bookingRepository.doCheckin(command) <------ PROBLEM HERE, HOW CAN I ACCESS THE COMMAND FROM ABOVE ??
                                .subscribeOn(Schedulers.from(threadExecutor))
                                .map(CheckinViewState.Success::new)
                                .cast(CheckinViewState.class)
                                .startWith(new CheckinViewState.LoadingState())
                                .onErrorReturn(CheckinViewState.ErrorState::new))
                        .observeOn(postExecutionThread.getScheduler());

        subscribeViewState(checkinViewStateObservable, CheckinView::render);
}

Observable<CnhImageResponse> uploadImage(Bitmap bitmap);

Моя проблема в том, что мой uploadImage возвращает внутреннюю структуру, которая заканчивается строкой, но, как я могу получить возвращенную строку, добавьте ее в мой command объект (установка возвращенного URL в этом объекте) и продолжение потока (отправка моей команды в облако)?

Спасибо!

1 ответ

Решение

Просто flatMap на наблюдаемой непосредственно в первой flatMap. В этом случае у вас есть ссылка на оба, checkinIntent и команду

 @Override
 protected void bindIntents() {
        Observable<CheckinViewState> checkinViewStateObservable =
                intent(CheckinView::sendCheckin)
                        .flatMap(checkinIntent -> { 
                          return imageRepository.uploadImage(checkinIntent.getSignature()
                                                .flatMap(imageResponse ->  bookingRepository.doCheckin(command) <-- Now you have access to both, command and CnhImageResponse 
                         }) 
                         .subscribeOn(Schedulers.from(threadExecutor))
                         .map(CheckinViewState.Success::new)
                         .cast(CheckinViewState.class)
                         .startWith(new CheckinViewState.LoadingState())
                         .onErrorReturn(CheckinViewState.ErrorState::new))
                         .observeOn(postExecutionThread.getScheduler());

        subscribeViewState(checkinViewStateObservable, CheckinView::render);
}

Альтернативное решение: пройти Pair<CheckinIntent, Command> к наблюдаемому от bookingRepository.doCheckin(...) как это:

@Override
protected void bindIntents() {
        Observable<CheckinViewState> checkinViewStateObservable =
                intent(CheckinView::sendCheckin)
                        .flatMap(checkinIntent -> imageRepository.uploadImage(checkinIntent.getSignature()
                                                                 .map(imageResponse -> Pair.create(checkinIntent, imageResponse))) // Returns a Pair<CheckinIntent, CnhImageResponse>
                        .flatMap(pair ->  bookingRepository.doCheckin(pair.first) <-- Now you can access the pair holding both information
                                .subscribeOn(Schedulers.from(threadExecutor))
                                .map(CheckinViewState.Success::new)
                                .cast(CheckinViewState.class)
                                .startWith(new CheckinViewState.LoadingState())
                                .onErrorReturn(CheckinViewState.ErrorState::new))
                        .observeOn(postExecutionThread.getScheduler());

        subscribeViewState(checkinViewStateObservable, CheckinView::render);
}

Просто несколько других заметок:

Вы почти всегда хотите отдать предпочтение switchMap() над flatMap() в МВИ. switchMap отписывается от предыдущей подписки, а flatMap - нет. Это означает, что если вы использовали flatMap, как вы делали в коде, и если по какой-то причине новый checkinIntent запущен, а старый еще не завершен (т. Е. ImageRepository.uploadImage() все еще выполняется), вы получите два потока это позвонит CheckinView::render потому что первый все еще продолжает работать и излучать результаты через ваш установленный наблюдаемый поток. switchMap() предотвращает это, отписав первое (незавершенное) намерение перед началом "switchMaping" нового намерения, чтобы у вас был только 1 поток за один раз.

То, как вы строите свой CheckinIntent должны быть перемещены в ведущий. Это слишком много логики для "дампа". Также Observable.just(BitmapFactory.decodeFile(...)) работает в главном потоке. Я рекомендую использовать Observable.fromCallable( () -> BitmapFactory.decodeFile(...)) поскольку последний откладывает свою "работу" (растровое декодирование) до тех пор, пока эта наблюдаемая не будет фактически подписана, а затем вы можете применять фоновые планировщики. Observable.just() в основном совпадает с:

Bitmap bitmap = BitmapFactory.decodeFile(...); // Here is the "hard work" already done, even if observable below is not subscribed at all.
Observable.just(bitmap);
Другие вопросы по тегам