Bir Gözlemlenebilirler listesini birleştirin ve tümü tamamlanana kadar bekleyin


91

TL; DR Nasıl dönüştürmek için Task.whenAll(List<Task>)içine RxJava?

Mevcut kodum, zaman uyumsuz görevlerin bir listesini oluşturmak için Bolts kullanıyor ve diğer adımları gerçekleştirmeden önce tüm bu görevlerin bitmesini bekler. Esasen, Bolts sitesindeki örneğe göre, listedeki tüm görevler tamamlandığında tamamlandı olarak işaretlenen List<Task>bir single oluşturur ve döndürür .Task

Ben yerine arıyorum Boltsile RxJavave zaman uyumsuz görevlerin listesini (değil önceden bilinen boyut) birikmesini ve tek içine hepsini sarma bu yöntemi assuming Observablemümkündür, ama nasıl olduğunu bilmiyorum.

Ben de bakarak denedim merge, zip, concatvb ... ama üzerinde çalışmaya alamayan List<Observable>hepsi sadece iki çalışan yönelik görünüyor gibi ben kadar bina edeceğimi ObservablesBen doğru docs anlarsanız bir anda.

Öğrenmeye çalışıyorum RxJavave hala çok yeniyim , bu yüzden bu bariz bir soruysa veya bir yerde belgelerde açıklanmışsa beni affedin; Aramayı denedim. Herhangi bir yardım çok takdir edilecektir.

Yanıtlar:


73

Zip operatörünü aradığınız anlaşılıyor .

Kullanmanın birkaç farklı yolu var, o yüzden bir örneğe bakalım. Diyelim ki farklı türlerde birkaç basit gözlemlenebilirimiz var:

Observable<Integer> obs1 = Observable.just(1);
Observable<String> obs2 = Observable.just("Blah");
Observable<Boolean> obs3 = Observable.just(true);

Hepsini beklemenin en basit yolu şuna benzer:

Observable.zip(obs1, obs2, obs3, (Integer i, String s, Boolean b) -> i + " " + s + " " + b)
.subscribe(str -> System.out.println(str));

Zip işlevinde, parametrelerin sıkıştırılmakta olan gözlemlenebilirlerin türlerine karşılık gelen somut türleri olduğunu unutmayın.

Bir gözlemlenebilirler listesinin sıkıştırılması da doğrudan mümkündür:

List<Observable<?>> obsList = Arrays.asList(obs1, obs2, obs3);

Observable.zip(obsList, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));

... veya listeyi şuna kaydırarak Observable<Observable<?>>:

Observable<Observable<?>> obsObs = Observable.from(obsList);

Observable.zip(obsObs, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));

Ancak, her iki durumda da, Object[]listedeki gözlemlenebilirlerin türleri ve sayıları önceden bilinmediğinden zip işlevi yalnızca tek bir parametreyi kabul edebilir . Bu, zip işlevinin parametre sayısını kontrol etmesi ve bunları uygun şekilde oluşturması gerektiği anlamına gelir.

Ne olursa olsun, yukarıdaki örneklerin tümü sonunda yazdırılacaktır 1 Blah true

DÜZENLEME: Zip kullanırken, Observablessıkıştırılan tüm öğelerin aynı sayıda öğe yaydığından emin olun . Yukarıdaki örneklerde her üç gözlemlenebilir tek bir öğe yaydı. Bunları böyle bir şeye değiştirirsek:

Observable<Integer> obs1 = Observable.from(new Integer[]{1,2,3}); //Emits three items
Observable<String> obs2 = Observable.from(new String[]{"Blah","Hello"}); //Emits two items
Observable<Boolean> obs3 = Observable.from(new Boolean[]{true,true}); //Emits two items

Sonra 1, Blah, Trueve 2, Hello, Truezip işlevlerine aktarılan tek öğe olacaktır. 3Diğer gözlenebilirler tamamlandığı için öğe asla sıkıştırılmayacaktır.


9
Çağrılardan biri başarısız olursa bu işe yaramaz. Bu durumda tüm çağrılar kaybedilecektir.
StarWind 0

1
@ StarWind0 kullanarak hatayı atlayabilirsiniz onErrorResumeNext, örnek:Observable.zip(ob1, ob2........).onErrorResumeNext(Observable.<String>empty())
vuhung3990

Ya 100 tane gözlemlenebilirim varsa?
Krzysztof Kubicki

80

flatMapDinamik görev kompozisyonunuz olması durumunda kullanabilirsiniz . Bunun gibi bir şey:

public Observable<Boolean> whenAll(List<Observable<Boolean>> tasks) {
    return Observable.from(tasks)
            //execute in parallel
            .flatMap(task -> task.observeOn(Schedulers.computation()))
            //wait, until all task are executed
            //be aware, all your observable should emit onComplemete event
            //otherwise you will wait forever
            .toList()
            //could implement more intelligent logic. eg. check that everything is successful
            .map(results -> true);
}

Paralel uygulamanın bir başka güzel örneği

Not: Hata işleme için gereksinimlerinizi gerçekten bilmiyorum. Örneğin, yalnızca bir görev başarısız olursa ne yapılmalı. Bu senaryoyu doğrulaman gerektiğini düşünüyorum.


17
Bu sorunun "listedeki tüm görevler tamamlandığında" durumları dikkate alınarak kabul edilen cevap olmalıdır. zipgörevlerden biri tamamlanır tamamlanmaz tamamlandığını bildirir ve bu nedenle uygulanamaz.
user3707125

1
@MyDogTom: Yanıtı Java7 Sözdizimi (lambda Değil) sürümüyle Güncelleyebilir misiniz?
sanedroid

3
@PoojaGaikwad Lambda ile daha okunabilir. Sadece ilk lambda değiştirmek new Func1<Observable<Boolean>, Observable<Boolean>>()...ve ikinci birnew Func1<List<Boolean>, Boolean>()
MyDogTom

@soshial RxJava 2, RxJava ile şimdiye kadarki en kötü şey, evet
egorikem

15

Önerilen önerilerden zip (), gerçekte gözlemlenebilir sonuçları birbiriyle birleştirir; bu, istenen olabilir veya olmayabilir, ancak soruda sorulmamıştır. Soruda istenen tek şey, işlemlerin her birinin tek tek veya paralel olarak yürütülmesiydi (bu belirtilmedi, ancak bağlantılı Bolts örneği paralel yürütmeyle ilgiliydi). Ayrıca zip (), gözlemlenebilirlerden herhangi biri tamamlandığında hemen tamamlanır, bu nedenle gereksinimleri ihlal eder.

Gözlemlenebilirlerin paralel yürütülmesi için, diğer yanıtta sunulan flatMap () iyidir, ancak merge () daha basit olacaktır. Birleştirme işleminin herhangi bir Gözlemlenebilir Öğede hata olması durumunda çıkacağını unutmayın; tüm gözlemlenebilirler bitene kadar çıkışı ertelemeyi tercih ederseniz, mergeDelayError () öğesine bakmalısınız .

Tek tek için Observable.concat () statik yönteminin kullanılması gerektiğini düşünüyorum. Javadoc şöyle ifade eder:

concat (java.lang.Iterable> sequences) Bir Gözlemlenebilir Öğenin Yinelemesini, birbiri ardına, onları harmanlamadan düzleştirir

paralel yürütme istemiyorsanız, peşinde olduğunuza benziyor.

Ayrıca, yalnızca görevinizin tamamlanmasıyla ilgileniyorsanız, değerleri döndürmekle ilgilenmiyorsanız, muhtemelen Gözlemlenebilir yerine Tamamlanabilir'e bakmalısınız .

TLDR: Görevlerin birer birer yürütülmesi ve tamamlandıklarında tamamlanma olayları için Completable.concat () en uygunudur. Paralel yürütme için, Completable.merge () veya Completable.mergeDelayError () çözüm gibi görünür. Birincisi, tamamlanabilir herhangi bir hata olduğunda derhal durur, ikincisi, birinde hata olsa bile hepsini çalıştırır ve ancak o zaman hatayı bildirir.


2

Muhtemelen zip2 Gözlemlenebilir ile çalışan operatöre bakmışsınızdır.

Statik yöntem de var Observable.zip. Sizin için yararlı olması gereken bir formu vardır:

zip(java.lang.Iterable<? extends Observable<?>> ws, FuncN<? extends R> zipFunction)

Daha fazlası için javadoc'a bakabilirsiniz .


2

Kotlin ile

Observable.zip(obs1, obs2, BiFunction { t1 : Boolean, t2:Boolean ->

})

İşlevin bağımsız değişkenlerinin türünü ayarlamak önemlidir, yoksa derleme hatalarınız olur

Son bağımsız değişken türü, bağımsız değişken sayısıyla değişir: BiFunction for 2 Function3 for 3 Function4 for 4 ...


1

JavaRx Observables ve RxKotlin ile Kotlin'de bazı hesaplama yükü kodu yazıyorum. Tamamlanacak bir gözlemlenebilirler listesi gözlemlemek ve bu arada bana ilerleme ve en son sonuçla ilgili bir güncelleme vermek istiyorum. Sonunda en iyi hesaplama sonucunu verir. Ekstra bir gereksinim, tüm cpu çekirdeklerimi kullanmak için Observables'ı paralel olarak çalıştırmaktı. Bu çözümü buldum:

@Volatile var results: MutableList<CalculationResult> = mutableListOf()

fun doALotOfCalculations(listOfCalculations: List<Calculation>): Observable<Pair<String, CalculationResult>> {

    return Observable.create { subscriber ->
        Observable.concatEager(listOfCalculations.map { calculation: Calculation ->
            doCalculation(calculation).subscribeOn(Schedulers.computation()) // function doCalculation returns an Observable with only one result
        }).subscribeBy(
            onNext = {
                results.add(it)
                subscriber.onNext(Pair("A calculation is ready", it))

            },
            onComplete = {
                subscriber.onNext(Pair("Finished: ${results.size}", findBestCalculation(results)) 
                subscriber.onComplete()
            },
            onError = {
                subscriber.onError(it)
            }
        )
    }
}

RxKotlin'e aşina değilsiniz @Volatile, ama bu aynı anda birkaç iş parçacığı tarafından çağrılıyorsa bu nasıl çalışır? Sonuçlara ne olur?
eis

0

Benzer bir sorun yaşadım, arama öğelerini dinlenme çağrısından almam ve ayrıca RecentSearchProvider.AUTHORITY'den kaydedilmiş önerileri entegre etmem ve bunları birleşik bir listede birleştirmem gerekiyordu. @MyDogTom çözümünü kullanmaya çalışıyordum, maalesef RxJava'da Observable.from yok. Biraz araştırdıktan sonra benim için çalışan bir çözüm buldum.

 fun getSearchedResultsSuggestions(context : Context, query : String) : Single<ArrayList<ArrayList<SearchItem>>>
{
    val fetchedItems = ArrayList<Observable<ArrayList<SearchItem>>>(0)
    fetchedItems.add(fetchSearchSuggestions(context,query).toObservable())
    fetchedItems.add(getSearchResults(query).toObservable())

    return Observable.fromArray(fetchedItems)
        .flatMapIterable { data->data }
        .flatMap {task -> task.observeOn(Schedulers.io())}
        .toList()
        .map { ArrayList(it) }
}

Sorguya bağlı olarak internetten öneri ve sonuç listelerini içeren bir gözlemlenebilirler dizisinden bir gözlemlenebilir yarattım. Bundan sonra, flatMapIterable ile bu görevlerin üzerinden geçin ve bunları flatmap kullanarak çalıştırın, sonuçları daha sonra bir geri dönüşüm görünümüne getirilebilecek bir diziye yerleştirin.


0

Project Reactor kullanıyorsanız kullanabilirsiniz Mono.when.

Mono.when(publisher1, publisher2)
.map(i-> {
    System.out.println("everything is done!");
    return i;
}).block()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.