在发送回调之前,请避免使用CountDownLatch等待许多线程

时间:2015-10-23 14:07:55

标签: java android multithreading retrofit rx-java

所以我有一个Track Id列表,对于每个轨道我需要执行网络请求以获取轨道详细信息,我使用for循环来启动所有请求和一个锁存器等待所有请求到完成。完成后,回调将与已填充的曲目列表一起发送。

我想知道是否有更好的方法可以做到这一点,也许是使用RxJava?

我在Android中使用Retrofit 2.0。

    public IBaseRequest batchTracksById(final TrackIdList trackIdListPayload, final IRequestListener<TracksList> listener) {
    final TracksList tracks = new TracksList();
    final Track[] trackArray = newrack[trackIdListPayload.getTrackIds().length];
    tracks.setTrack(trackArray);

    final CountDownLatch latch = new CountDownLatch(trackArray.length);


    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                latch.await();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onRequestUpdate(null, tracks, null, true);
                    }
                });

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t.start();

    for (String id : trackIdListPayload.getTrackIds()) {
        getTrackById(id, new IRequestListener<Track>() {
            @Override
            public void onRequestFailure(IBaseRequest request, Exception exception) {
                latch.countDown();
            }

            @Override
            public void onRequestUpdate(IBaseRequest request, Track track, RequestState state, boolean requestComplete) {
                //iterate through the tracks and update the ones in the thing

                int i = 0;
                for (String s : trackIdListPayload.getTrackIds()) {
                    if (s.equals(track.getTrackId())) {
                        trackArray[i] = track;
                        // don't break here, as we may have a case where we have multiple instances of the same trackId (although
                        // at the moment a request will be made for each anyway...
                    }
                    i++;
                }

                latch.countDown();
            }
        });
    }

    return null;
}

2 个答案:

答案 0 :(得分:1)

如果我理解正确,您有一个轨道列表作为输入,您需要一个Web服务结果列表。如果您可以使网络调用同步(rxjava将为您处理后台处理),这是使用RxJava执行此操作的简单方法。

 Observable.from(trackList)
                    .map(new Func1<Track, Response>() {
                        @Override
                        public Response call(Track track) {
                            return makeRequestSynchronously(track.id());
                        }
                    })
                    .toList()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Subscriber<List<Response>>() {

                        @Override
                        public void onCompleted() {

                        }

                        @Override
                        public void onError(Throwable e) {

                        }

                        @Override
                        public void onNext(List<Response> responses) {

                        }
                    });

编辑:您可以更改Retrofit以从webservice返回observable,如果您这样做,则需要将map更改为以下内容

 .flatMap(new Func1<Track, Observable<Response>>() {
                    @Override
                    public Observable<Response> call(Track track) {
                        return makeRequestThatReturnsObservable(track.id());
                    }
                })

答案 1 :(得分:1)

如果你想异步地发出所有请求并等待它们返回,你可以这样做(lambdas为了简洁和可读性):

tracks.flatMap(track -> getTrackDetails(track.id)
                          .subscribeOn(Schedulers.io()))
      .toList()
      .doOnNext(list -> processTrackList())
      ...

如果您要求以tracks的顺序返回结果,但仍然是异步请求,那么很快就会发布rxjava 1.0.15,您将能够执行此操作

tracks.concatMapEager(track -> getTrackDetails(track.id)
                          .subscribeOn(Schedulers.io())
      .toList()
      .doOnNext(list -> processTrackList())
      ...