如何在 RxJava 的 Observable 中的 map() 方法中处理异常?

20

I want to do this:

Observable.just(bitmap)
            .map(new Func1<Bitmap, File>() {
                @Override
                public File call(Bitmap photoBitmap) {

                    //File creation throws IOException, 
                    //I just want it to hit the onError() inside subscribe()

                    File photoFile = new File(App.getAppContext().getCacheDir(), "userprofilepic_temp.jpg");
                    if(photoFile.isFile()) {//delete the file first if it exists otherwise the new file won't be created
                        photoFile.delete();
                    }
                    photoFile.createNewFile(); //saves the file in the cache dir

                    FileOutputStream fos = new FileOutputStream(photoFile);
                    photoBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);//jpeg format
                    fos.close();

                    return photoFile;

                }
            })
            .subscribe(//continue implementation...);

call()方法中,可能会抛出异常。我该如何让观察者在onError()中处理它呢?或者这不是正确的思考方式?


1
请注意,在RxJava 2中,像map这样的操作符允许从lambda表达式中抛出已检查异常。这在RxJava 1中实际上是一个设计缺陷,因为无法将抛出的确切错误传播到map lambda中的'onError',除非将其包装为RuntimeException - Dave Moten
4个回答

24

即使是RuntimeException,rx也将捕获错误。因此,您可以在catch块中抛出某种运行时异常。实际上,这就是它应该工作的方式。

 Observable.just(bitmap)
                .map(b -> {
                    try {
                        // do some work which throws IOException
                        throw new IOException("something went wrong");
                    } catch (IOException e) {
                        throw new RXIOException(e);
                        // Or you can use 
                        throw Exceptions.propagate(e);
                        // This helper method will wrap your exception with runtime one
                    }
                }).subscribe(o -> {
                    // do something here
                }, exception -> exception.printStackTrace());

public static class RXIOException extends RuntimeException {
        public RXIOException(IOException throwable) {
            super(throwable);
        }
}

1
当我尝试这样做时,它要求我用try-catch包围异常。 - Sree
2
你必须抛出RuntimeException。JVM不会要求你将其包围在try catch中。这就是想法。 - wnc_21
3
来吧,IOException不是运行时异常。请再阅读一遍我的回答。 - wnc_21
啊,抱歉。听起来很有道理。您能看一下 @akarnokd 上面的答案吗?他使用了 Observable.error。您认为您的方法比他的更好吗? - Sree
这取决于你要做什么。Observable.error() 返回新的 Observable,只能与 flatMap 一起使用。如果你想在以下情况下使用 mObservable.map(某些可能会抛出错误的内容).filter(异常信息).subscribe(...),则无法使用 Observable.error()。 - wnc_21
显示剩余3条评论

8

在1.0.15版本中,新增了fromCallable工厂方法,它允许你为每个订阅者运行一个Callable实例,在这里你可以抛出已检查的异常:

Observable.fromCallable(() -> {      
    File photoFile = new File(App.getAppContext().getCacheDir(),
        "userprofilepic_temp.jpg");
    if (photoFile.isFile()) {
       //delete the file if it exists otherwise the new file won't be created
        photoFile.delete();
    }
    photoFile.createNewFile(); //saves the file in the cache dir

    FileOutputStream fos = new FileOutputStream(photoFile);
    photoBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);//jpeg format
    fos.close();

    return photoFile;
})
.subscribe(...)

编辑:

source.flatMap(v -> {
    try {
        //...
        return Observable.just(result);
    } catch (Exception e) {
        return Observable.error(e);
    }
})
.subscribe(...);

我猜这对这种情况可能有效,但如果我想链接两个依赖于第一个映射输出的映射怎么办?使用 Func1() 我可以获取返回类型和参数。 - Sree
你必须使用flatMap、try-catch并返回just()或error()。 - akarnokd
运行得像冠军一样!! - Raja Jawahar

3

刚刚创建了帮助类,将这个样板代码提取到另一个地方:

public class RxRethrow {
    public static <T, R> Func1<T, R> rethrow(Func1R<T, R> catchedFunc) {
        return t -> {
            try {
                return catchedFunc.call(t);
            } catch (Exception e) {
                throw Exceptions.propagate(e);
            }
        };
    }

    public interface Func1R<T, R> extends Function {
        R call(T t) throws Exception;
    }
}

您可以这样调用:
.map(RxRethrow.rethrow(products -> mapper.writer(schema).writeValueAsString(products)))

3
我不知道这个问题最初提出和回答时的情况如何,但是RxJava目前包含一个用于此特定目的的辅助方法:Exceptions.propagate(Throwable t)

RxJava Javadoc

方便的方法,可以直接抛出RuntimeException和Error,或将任何其他异常类型包装为RuntimeException。


@Sree 哇,我居然没注意到,我被自定义异常类蒙住了眼睛。现在仔细看,我可以看到这里实际上有另一个使用相同方法的答案。 - Thorbear

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接