使用RxJava Observables

时间:2015-07-07 12:46:06

标签: java android rx-java

我尝试并行下载图片列表,并将它们合并到地图上。

起初我尝试制作一个像这样的Observable:

Observable<Map<Integer, Bitmap>> getImages(final List<Activity> activities) {
    return Observable.create(new Observable.OnSubscribe<Map<Integer, Bitmap>>() {
        @Override
        public void call(Subscriber<? super Map<Integer, Bitmap>> subscriber) {
            try {
                Map<Integer, Bitmap> result = new HashMap<Integer, Bitmap>();
                for (Activity act : activities) {
                    result.put(act.getId(), downloadImage(act.getImage()));
                }
                subscriber.onNext(result);
                subscriber.onCompleted();
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    });
}

这样可行,但这不是我想要的。因为图像是按顺序下载的。我认为for循环在rxjava中并不好用。所以我创建了这些Observables:

    Observable<Bitmap> getImage(final Activity activity) {
    return Observable.create(new Observable.OnSubscribe<Bitmap>() {
        @Override
        public void call(Subscriber<? super Bitmap> subscriber) {
            try {
                subscriber.onNext(downloadImage(activity.getImage()));
                subscriber.onCompleted();
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    });
}

Observable<Map<Integer, Bitmap>> getImages(final List<Activity> activities) {
    return Observable
        .from(activities)
        .flatMap(new Func1<Activity, Observable<Bitmap>>() {
            @Override
            public Observable<Bitmap> call(Activity activity) {
                return getImage(activity);
            }
        })
        .toMap(new Func1<Bitmap, Integer>() {
            @Override
            public Integer call(Bitmap bitmap) {
                return 1; // How am I supposed to get the activity.getId()?
            }
        });
}

所以我制作了一个Observable来获取单个图像,尝试使用flatMap将它们组合在第二个图像中。这有效,但仍然存在两个问题:

  1. 当我执行toMap()时,如何检索正确的id以用作地图的键?我想使用Activity对象。
  2. 不幸的是,下载仍然按顺序处理,而不是并行处理。我该如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

创建一个包含位图和Activity的包装类。说ActivityBitmap。将getImage替换为getActivityBitmap

Observable<ActivityBitmap> getActivityBitmap(final Activity activity) {
    return Observable.create(new Observable.OnSubscribe<ActivityBitmap>() {
        @Override
        public void call(Subscriber<? super ActivityBitmap> subscriber) {
            try {
                subscriber.onNext(new ActivityBitmap(activity, downloadImage(activity.getImage())));
                subscriber.onCompleted();
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    });
}

并在下面调用它。请注意,要获得异步下载,请在subscribeOn中使用flatMap。要在最后构建Map<Integer,Bitmap>,您可以使用toMap的不同重载来指定键和值。

Observable<Map<Integer, Bitmap>> getImages(final List<Activity> activities) {
    return Observable
        .from(activities)
        .flatMap(new Func1<Activity, Observable<ActivityBitmap>>() {
            @Override
            public Observable<ActivityBitmap> call(Activity activity) {
                return getActivityBitmap(activity).subscribeOn(Schedulers.io());
            }
        })
        .toMap(new Func1<ActivityBitmap, Integer>() {
            @Override
            public Integer call(ActivityBitmap activityBitmap) {
                return activityBitmap.getActivity().getId();
            }
        },new Func1<ActivityBitmap, Bitmap>() {
            @Override
            public Integer call(ActivityBitmap activityBitmap) {
                return activityBitmap.getBitmap();
            }
        });
}

答案 1 :(得分:1)

我有一个可能的解决方案。它使用reduce运算符转换为地图。虽然,我不确定订阅Observable中的Observable是不错的做法。

Observable<Bitmap> getImage(final Activity activity) {
    return Observable.create(new Observable.OnSubscribe<Bitmap>() {
        @Override
        public void call(Subscriber<? super Bitmap> subscriber) {
            try {
                subscriber.onNext(downloadImage(activity.getImage()));
                subscriber.onCompleted();
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    });
}

Observable<HashMap<Integer, Bitmap>> getImages(final List<Activity> activities) {
    return Observable
        .from(activities)
        .reduce(new HashMap<Integer, Bitmap>(), new Func2<HashMap<Integer, Bitmap>, Activity, HashMap<Integer, Bitmap>>() {
            @Override
            public HashMap<Integer, Bitmap> call(final HashMap<Integer, Bitmap> bitmaps, final Activity activity) {
                getImage(activity)
                    .observeOn(Schedulers.io())
                    .subscribeOn(Schedulers.io())
                    .subscribe(new Action1<Bitmap>() {
                        @Override
                        public void call(Bitmap bitmap) {
                            bitmaps.put(activity.getId(), bitmap);
                        }
                    });
                return bitmaps;
            }
        });
}

非常感谢对此解决方案的反馈。

答案 2 :(得分:0)

我会那样:

Observable<Map<Integer, Bitmap>> getImages(List<Activity> activities) {
    return Observable.from(activities)
          .map(activity -> new Pair(activity.getId(), downloadImage(activity.getImage())))
          .toMap(pair -> pair.first, pair -> pair.second);
}

(nota:我使用retrolambda,因此是lambdas)

显然,类似的东西应该是平行的:

Observable.from(activities)
          .flatMap(activity ->
                Observable.zip(Observable.just(activity.getId(),
                               downloadImage(activity.getImage())),
                (k, v) -> new Pair(k, v)))
          .toMap(pair -> pair.first, pair -> pair.second);

(前提是downloadImage返回异步Observable