【问题标题】:How to make multiple requests using retrofit2 and rxjava2 on android?如何在android上使用retrofit2和rxjava2发出多个请求?
【发布时间】:2019-06-20 21:01:25
【问题描述】:

在我的 android 应用程序中,我想使用改造和 rxjava 发出多个 http 请求来获取 json 数据。请求的数量取决于用户的偏好(1 到 40)。每个请求都是独立的并且返回相同的类型。所以,我尝试应用在这个问题 (How to make multiple request and wait until data is come from all the requests in retrofit 2.0 - android) 中推荐的方式,它使用 rx-java 的 zip 功能。但我找不到一种方法来获取和组合每个请求的结果。我在改造中用于单个请求的响应类型是Response<List<NewsItem>>,其中 NewsItem 是我的自定义对象。 (响应实际上是 json 数组,但在单个请求中改造会自动处理它并将其转换为我的自定义对象列表)到目前为止我尝试的内容如下:

我的 API 接口

public interface API {

    String BASE_URL = "xxx/";

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

Viewmodel 类获取数据

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();

    }
}

它没有给出错误,但也没有给出响应,因为我无法处理响应。任何人都可以帮助我组合每个请求的结果吗?我搜索了所有问题,但找不到这样的示例。

【问题讨论】:

    标签: android rx-java retrofit2 rx-java2


    【解决方案1】:

    Object[] objects 是请求返回的项目数组。假设您的每个请求都返回一个List&lt;NewsItem&gt;,并且您想将所有NewsItem 合并为一个List&lt;NewsItem&gt;,我认为您可以按照以下方式进行操作:

    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())
            ...
    }
    

    注意类型转换。

    【讨论】:

    • 谢谢@Sanlok,我试过但没有用。但这让我对如何填充方法内部有了一些想法。此外,您建议的演员表提醒Unchecked cast: 'java.lang.Object' to 'java.util.List&lt;NewsItem&gt; 我认为问题与响应类型有关。在我使用 Retrofit 发出的单个请求中,retrofit 会生成它的原始响应,类型为 Response&lt;List&lt;NewsItem&gt;&gt;,然后您可以使用 response.body 方法将其转换为 List&lt;NewsItem&gt;。但是现在我不知道该怎么办?
    • 您能告诉我们您所说的“它不起作用”是什么意思吗?它崩溃了吗?你试过记录吗?
    • 我也不确定你所说的Response&lt;List&lt;NewsItem&gt;&gt; 是什么意思,因为你的“我的API 接口”说它返回一个Observable&lt;List&lt;NewsItem&gt;&gt;。你能发布你更新的代码吗?
    • 抱歉误会,在单个 api 调用中(我之前使用过)当然 api 接口是这样的:Call&lt;List&lt;NewsItem&gt;&gt;。我的意思是改造返回的响应类型为Response&lt;List&lt;NewsItem&gt;&gt;,这可能是关键因素。当我尝试你的答案时它没有崩溃,但没有任何改变。
    【解决方案2】:

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

    【讨论】:

    • 谢谢,可能是这样,但我从你的建议中了解到,它不会是平行的,这会增加返回时间。
    【解决方案3】:

    尝试使用Observable.from(Iterable&lt;? extends T&gt; 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更新了方法

    【讨论】:

    • 感谢@Boris,但实际上我是 RxJava 的新手,你最好重写,而且我的 android studio 也不支持lambas。
    • 在 Rx-java2 的情况下,您可以使用 Flowable.fromIterable(mySourceList) 而不是 Observable.from(mySourceList) :)
    • 我已经在你的帮助下更新了方法(我已经解决了 rx-java 库问题)但是现在,当我调试时,我发现它没有执行 .map 方法,所以还是不行。
    • @HuseyinSahin 你确定.unsubscribe() 吗?对我来说,订阅后立即取消订阅看起来很奇怪。第二个问题:你确定所有api.getGundemNews(source) 请求都被执行了吗?原因 .toList 等待所有项目执行
    • 我稍后放了它,之前也没有工作(当 livedata 获取所需的项目时,我认为退订是安全的)但是好的,我已经删除了它。
    猜你喜欢
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 2018-02-13
    相关资源
    最近更新 更多