简体   繁体   中英

RxJava Load items on demand

I want to load the next item(s) when some other observable emits a new item (a trigger).

This is the code that emits the items:

public Observable<Item> get() {
    return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
        @Override
        public ObservableSource<String> apply(@NonNull List<String> ids) throws Exception {
            return Observable.fromIterable(ids);
        }
    }).flatMap(new Function<String, ObservableSource<Item>>() {
        @Override
        public ObservableSource<Item> apply(@NonNull final String id) throws Exception {
            return dataApi.get(id).map(new Function<Data, Item>() {
                @Override
                public Item apply(@NonNull Data data) throws Exception {
                    return new Item(data , id);
            });
        }
    });
}

Trigger Observable:

RxView.clicks(view.findViewById(R.id.button_more)).debounce(500, TimeUnit.MILLISECONDS);

The only way I could get around this was using a Subject and holding a reference to a list of ids which wasn't elegant and didn't seem reactive.

Edit: This is my solution so far, but I had to subscribe to trigger event directly. I don't consider it elegant.

@Override
public Observable<Item> get(final Observable<Object> trigger) {
    final PublishSubject<Item> subject = PublishSubject.create();
    return idApi.get().flatMap(new Function<List<String>, ObservableSource<Queue<String>>>() {
        @Override
        public ObservableSource<Queue<String>> apply(@NonNull List<String> ids) throws Exception {
            final Queue<String> q = new LinkedList<>(ids);
            return Observable.just(q);
        }
    }).flatMap(new Function<Queue<String>, ObservableSource<Item>>() {
        @Override
        public ObservableSource<Item> apply(@NonNull final Queue<String> ids) throws Exception {
            trigger.subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Object>() {
                @Override
                public void accept(@NonNull Object o) throws Exception {
                    if (ids.size() > 0) {
                        final String id = ids.poll();
                        dataApi.get(id).map(new Function<Data, Item>() {
                            @Override
                            public Item apply(@NonNull Data data) throws Exception {
                                return new Item(data, id) l
                            }
                        }).subscribe(new Consumer<Item>() {
                            @Override
                            public void accept(@NonNull Item item) throws Exception {
                                subject.onNext(item);
                            }
                        });
                    } else {
                        subject.onComplete();

                    }
                }
            });
            return subject;
        }
    });
}

Use zip

public Observable<Item> get(View v) {
    return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
        @Override
        public ObservableSource<String> apply(@NonNull List<String> ids) throws Exception {
            return Observable.fromIterable(ids);
        }
    }).zipWith(RxView.clicks(v).debounce(500, TimeUnit.MILLISECONDS), (n, i) -> n))    
    .flatMap(new Function<String, ObservableSource<Item>>() {
        @Override
        public ObservableSource<Item> apply(@NonNull final String id) throws Exception {
            return dataApi.get(id).map(new Function<Data, Item>() {
                @Override
                public Item apply(@NonNull Data data) throws Exception {
                    return new Item(data , id);
            });
        }
    });
}

to get N items with each click

public Observable<Item> getN(View v, int nitems) {
    return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
            @Override
            public ObservableSource<String> apply(@NonNull List<String> ids) throws Exception {
                return Observable.fromIterable(ids);
            }
        }).buffer(nitems).zipWith(RxView.clicks(v).debounce(500, TimeUnit.MILLISECONDS), (n, i) -> n))
        .flatMap(new Function<List<String>, ObservableSource<String>>() {
            @Override
            public ObservableSource<String> apply(@NonNull final List<String> ids) throws Exception {
                return Observable.from(ids)
            }
         }
        )  
        .flatMap(new Function<String, ObservableSource<Item>>() {
            @Override
            public ObservableSource<Item> apply(@NonNull final String id) throws Exception {
                return dataApi.get(id).map(new Function<Data, Item>() {
                    @Override
                    public Item apply(@NonNull Data data) throws Exception {
                        return new Item(data , id);
                 });
               }
         }
        });
 }

Edit: You will still have to use subscribeOn to make sure you are on the main thread for RXView.clicks and on the IO thread for any networking.

Not so good but it works:

your get method:

Observable<Item> get(final String id){
    return Observable.defer(() -> {
    dataApi.get(id).map(new Function<Data, Item>() {
            @Override
            public Item apply(@NonNull Data data) throws Exception {
                return new Item(data , id);
        });
    })
}

your click:

private List<String> ids = {//your id s}
private int current = 0;

RxView.clicks(view).flatmap(ignore -> get(ids.get(current++)))
                   .subscribe(//your Observer)

I'd like recommend that answer with zipwith() , seems better than my answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM