简体   繁体   English

如何在android上使用retrofit2和rxjava2发出多个请求?

[英]How to make multiple requests using retrofit2 and rxjava2 on android?

In my android app, I want to make multiple http requests using retrofit and rxjava to fetch json data.在我的 android 应用程序中,我想使用改造和 rxjava 发出多个 http 请求来获取 json 数据。 The number of request depends on the user's preferences (1 up to 40).请求的数量取决于用户的偏好(1 到 40)。 Each request is independent and returns same type.每个请求都是独立的并返回相同的类型。 So, i tried to apply the way that is recommended in this question ( How to make multiple request and wait until data is come from all the requests in retrofit 2.0 - android ) which uses the zip function of rx-java.因此,我尝试应用此问题中推荐的方式( 如何发出多个请求并等待数据来自改造 2.0 - android 中的所有请求),该方式使用 rx-java 的 zip 功能。 But i couldn't find a way to get and combine the results of each request.但是我找不到一种方法来获取和组合每个请求的结果。 Response type that i used for single request in retrofit was Response<List<NewsItem>> where NewsItem is my custom object.我在改造中用于单个请求的响应类型是Response<List<NewsItem>>其中 NewsItem 是我的自定义对象。 (the response is json array actually but in a single request retrofit automatically handles it and converts it into list of my custom object) What i tried so far is below: (响应实际上是 json 数组,但在单个请求改造中会自动处理它并将其转换为我的自定义对象列表)到目前为止我尝试过的内容如下:

My API Interface我的API接口

public interface API {

    String BASE_URL = "xxx/";

    @GET("news/{source}")
    Observable<List<NewsItem>> getNews(@Path("source") String source);
}

Viewmodel class to fetch data获取数据的视图模型类

public class NewsVM extends AndroidViewModel {

    public NewsVM(Application application){
        super(application);
    }

    private MutableLiveData<List<NewsItem>> newsLiveData;

    public LiveData<List<NewsItem>> getNewsLiveData(ArrayList<String> mySourceList) {

        newsLiveData = new MutableLiveData<>();
        loadNews(mySourceList);

        return newsLiveData;
    }

    private void loadNews(ArrayList<String> mySourceList) {

        Gson gson = new GsonBuilder().setLenient().create();

        Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(API.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();

        API api = retrofit.create(API.class);

        //Gathering the requests into list of observables
        List<Observable<?>> requests = new ArrayList<>();
        for(String source: mySourceList){
            requests.add(api.getNews(source));
        }

        // Zip all requests
        Observable.zip(requests, new Function<Object[], List<NewsItem>>() {
            @Override
            public List<NewsItem> apply(Object[] objects) throws Exception {

                // I am not sure about the parameters and return type in here, probably wrong 
                return new ArrayList<>();
            }
        })
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.newThread())
            .subscribe(
                new Consumer<List<NewsItem>>() {
                    @Override
                    public void accept(List<NewsItem> newsList) throws Exception {

                        Log.d("ONRESPONSE",newsList.toString());
                        newsLiveData.setValue(newsList);
                    }
                },
                new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable e) throws Exception {

                        Log.d("ONFAILURE", e.getMessage());
                    }
                }
        ).dispose();

    }
}

It doesn't give error but doesn't give response also since I couldn't handle the response.它没有给出错误,但也没有给出响应,因为我无法处理响应。 Can anybody help me in combining the results of the each request?有人可以帮我合并每个请求的结果吗? I've searched all the questions but can't find an example like this.我已经搜索了所有问题,但找不到这样的例子。

try to use Observable.from(Iterable<? extends T> iterable) ( Observable.fromArray() in rx-java2) instead of zip So you'll have something like:尝试使用Observable.from(Iterable<? extends T> iterable) ( rx-java2 中的Observable.fromArray() )而不是zip所以你会有类似的东西:

Observable.from(mySourceList)
    .flatMap(new Func1<String, Observable<List<NewsItem>>>() {
        @Override
           public Observable<List<NewsItem>> call(String source) {
                return api.getNews(source);
            }
        })
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.newThread())
        .toList() // This will give you List<List<NewsItem>>
        .map(new Func1<List<List<NewsItem>>, List<NewsItem>>() {
            @Override
            public List<NewsItem> call(List<List<NewsItem>> listOfList) {
                //Merged list of lists to single list using Guava Library
                List<NewsItem> list = Lists.newArrayList(Iterables.concat(listOfList));
                return list;
            }
        })
        .subscribe(new Subscriber<List<NewsItem>>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }

            @Override
            public void onNext(List<NewsItem> newsList) {
                //Attached the final newslist to livedata
                newsLiveData.setValue(newsList);
            }
        });

EDITED Updated the method已编辑更新了方法

Object[] objects is the array of items returned by the requests. Object[] objects是请求返回的项目数组。 Assuming that each of your request returns a List<NewsItem> and you wanted to combine all NewsItem into a single List<NewsItem> , I think you can do something along the lines of:假设您的每个请求都返回一个List<NewsItem>并且您想将所有NewsItem合并到一个List<NewsItem> ,我认为您可以执行以下操作:

private void loadNews(ArrayList<String> mySourceList) {
    ...
    // Zip all requests
    Observable.zip(requests, new Function<Object[], List<NewsItem>>() {
        @Override
        public List<NewsItem> apply(Object[] objects) throws Exception {
            List<NewsItem> combinedNewsItems = new ArrayList<>();
            for (Object response : objects) {
                combinedNewsItems.addAll((List<NewsItem>) response);
            }
            return combinedNewsItems;
        }
    })
        .subscribeOn(Schedulers.io())
        ...
}

Be aware of the type casting.注意类型转换。

如果您获取的数据类型是相同的List并且请求是多个,那么您可以使用递归方法进行 1 到 n 个请求,并在每次成功时添加数据List

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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