避免在发送回调之前使用 CountDownLatch 等待多个线程
Avoid using CountDownLatch to wait for many threads before sending the callback
所以我有一个轨道 ID 列表,对于每个轨道 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;
}
如果我没理解错的话,你有一个曲目列表作为输入,你想要一个网络服务结果列表。如果您可以使您的网络调用同步(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 更改为来自网络服务的 return observables,如果这样做,您需要将地图更改为以下内容
.flatMap(new Func1<Track, Observable<Response>>() {
@Override
public Observable<Response> call(Track track) {
return makeRequestThatReturnsObservable(track.id());
}
})
如果您想异步发出所有请求并等待它们 return 您可以这样做(为了简洁和可读性,lambdas):
tracks.flatMap(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io()))
.toList()
.doOnNext(list -> processTrackList())
...
如果您要求结果按 tracks
的顺序 return 编辑,但仍然是异步请求,那么在即将发布的 rxjava 1.0.15 中,您将能够做到这一点
tracks.concatMapEager(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io())
.toList()
.doOnNext(list -> processTrackList())
...
所以我有一个轨道 ID 列表,对于每个轨道 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;
}
如果我没理解错的话,你有一个曲目列表作为输入,你想要一个网络服务结果列表。如果您可以使您的网络调用同步(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 更改为来自网络服务的 return observables,如果这样做,您需要将地图更改为以下内容
.flatMap(new Func1<Track, Observable<Response>>() {
@Override
public Observable<Response> call(Track track) {
return makeRequestThatReturnsObservable(track.id());
}
})
如果您想异步发出所有请求并等待它们 return 您可以这样做(为了简洁和可读性,lambdas):
tracks.flatMap(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io()))
.toList()
.doOnNext(list -> processTrackList())
...
如果您要求结果按 tracks
的顺序 return 编辑,但仍然是异步请求,那么在即将发布的 rxjava 1.0.15 中,您将能够做到这一点
tracks.concatMapEager(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io())
.toList()
.doOnNext(list -> processTrackList())
...