У RxJava, як передавати змінну при ланцюжку спостережуваних?


80

Я прив'язую операції асинхронізації за допомогою RxJava, і я хотів би передати деяку змінну нижче:

Observable
   .from(modifications)
   .flatmap( (data1) -> { return op1(data1); })
   ...
   .flatmap( (data2) -> { 
       // How to access data1 here ?
       return op2(data2);
   })

Це схоже на загальну закономірність, але я не міг знайти про це інформацію.

Відповіді:


65

Порада, яку я отримав від форуму Couchbase, полягає у використанні вкладених спостережуваних:

Observable
   .from(modifications)
   .flatmap( (data1) -> { 
       return op1(data1)
           ...
           .flatmap( (data2) -> { 
               // I can access data1 here
               return op2(data2);
           })
   });

РЕДАГУВАТИ: Я позначу це як прийняту відповідь, оскільки, здається, це найбільш рекомендується. Якщо ваша обробка занадто складна, щоб вкласти все, ви також можете перевірити рішення за допомогою викликів функцій.


Це те, що я часто роблю, і дотепер працював чудово.
Буде

Хм, це мало чим відрізняється, якщо порівнювати зі "зберіганням даних поза потоком" для мене.
рор

18

Інша можливість полягає у відображенні результату op1в, org.apache.commons.lang3.tuple.Pairщо містить змінну, і передачі цього:

Observable
   .from(modifications)
   .flatmap( (data1) -> {
       return op1(data1).map( obj -> { return Pair.of(data1,obj); });
   })
   ...
   .flatmap( (dataPair) -> { 
       // data1 is dataPair.getLeft()
       return op2(dataPair.getRight());
   })

Це працює, але відчуває себе трохи незручно, коли змінні приховані всередині Pair / Triple / ... і стає дуже багатослівним, якщо ви використовуєте позначення Java 6.

Цікаво, чи є краще рішення, можливо, якийсь оператор RxJava може допомогти?


2
Я не думаю, що в цьому є щось погане, але коли я відчуваю, що мені потрібно дотягнутися до класу Pair, я також відчуваю, що роблю щось не так. Кількість разів, коли я користувався ним, а потім повторно врахував його згодом, коли я краще зрозумів свій домен.
Буде

Це рішення, яке прийшло до мене, хоча воно створило б тонну Pairекземплярів для сміття, щоб тримати data1поруч obj. Цікаво, чи combineспрацює a .
scorpiodawg

7

flatmap може взяти другий аргумент:

Observable.just("foo")
                .flatMap(foo -> Observable.range(1, 5), Pair::of)
                .subscribe(pair -> System.out.println("Result: " + pair.getFirst() + " Foo: " + pair.getSecond()));

джерело: https://medium.com/rxjava-tidbits/rxjava-tidbits-1-use-flatmap-and-retain-original-source-value-4ec6a2de52d4


як імпортувати пару?
Dyno Cris

@DynoCris ти можеш написати свій власний або імпортувати його з деяких бібліотек (просто google java + пара)
Сізіф

6

Однією з можливостей було б використовувати виклик функції:

private static Observable<T> myFunc(final Object data1) {
    return op1(data1)
        ...
        .flatmap( (data2) -> { 
            // I can access data1 here
            return op2(data2);
        });
}

Observable
   .from(modifications)
   .flatmap( (data1) -> { return myFunc(data1); })

АЛЕ: виправте мене, якщо я помиляюся, але це не схоже на реактивне програмування


2
Чи це значно відрізняється від вашого першого варіанту?
Буде

@ Буде так, я думаю, це майже аналогічно використанню вкладених викликів. Для складної обробки це, мабуть, не поганий вибір.
Джуліан Го

3

Насправді у нас є бібліотека, яка спрощує ланцюжки викликів.

https://github.com/pakoito/Komprehension

Додавання як залежності Gradle:

implementation 'io.reactivex.rxjava2:rxjava:2.2.1'
implementation 'com.github.pakoito.Komprehensions:komprehensions-rx2:1.3.2'

Використання (Kotlin):

val observable = doFlatMap(
    { Observable.from(modifications) },
    { data1 -> op1(data1) },
    { data1, data2 -> op2(data2) },
    { data1, data2, data3 -> op3(data1, data2, data3) }
)

0

рішення у цьому потоці працює, але для складних ланцюжків це ускладнює читання коду, мені довелося передати кілька значень, і те, що я зробив, це створити приватний клас з усіма параметрами, і я вважаю, що код таким чином стає більш читабельним,

private class CommonData{
   private string data1;
   private string data2;

   *getters and setters*
}
...
final CommonData data = new CommonData();
Observable
   .from(modifications)
   .flatmap( (data1) -> { 
       data.setData1(data1);
       return op1(data1); 
   })
   ...
   .flatmap( (data2) -> { 
       data2 = data.getData1() + "data 2... ";
       data.setData2(data2);
       return op2(data2);
   })

сподіваюся, це допоможе


2
На жаль, це, мабуть, не є безпечним для потоків (залежно від вашого коду, RxJava буде виконувати обчислення на декількох потоках)
Джуліан Го

я розгублений, чому приватна, нестатична змінна буде потоком - "небезпечною"
josesuero

1
Якщо RxJava створює декілька потоків для створення flatmap () ваших даних, екземпляр CommonData буде спільним для потоків, навіть якщо він не є статичним. (Це повинно перевірятися з деякими журналами, які відображають поточний потік та значення CommonData)
Джуліан Го

Мені потрібно знайти інше рішення, тому що якщо вам доведеться зробити щось на зразок: if (логічне) return observer1 else return observer2; вам довелося б скласти карти на обох, що було б .. погано
josesuero

0

Я знаю, що це старе запитання, але використовуючи RxJava2 і лямбда, ви можете використовувати щось на зразок:

Observable
.from(modifications)
.flatMap((Function<Data1, ObservableSource<Data2>>) data1 -> {
                        //Get data 2 obeservable

                            return Observable.just(new Data2())
                        }
                    }, Pair::of)

На наступному потоці (плоска карта / карта) ваша вихідна пара буде (data1, data2)


-2

Ви можете використовувати "глобальну" змінну, щоб отримати це:

 Object[] data1Wrapper = new Object[]{null};
 Object[] data2Wrapper = new Object[]{null};
 Observable
    .from(modifications)
    .flatmap(data1 -> {
        data1Wrapper[0] = data1;
        return op1(data1)
     })
      ...
    .flatmap(data2 -> { 
        // I can access data1 here use data1Wrapper[0]
        Object data1 = data1Wrapper[0];
        data2Wrapper[0] = data2;
        return op2(data2);
     })

4
Це погана порада, не зберігайте дані поза потоком, використовуйте SwitchMap або подібний підхід для передачі даних. Це протиузор.
лсром
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.