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

Basically in the call() method, it can throw exceptions. How can I make the Observer handle it in onError(). Or is this not the right way to think about this?

greatwolf
  • 20,287
  • 13
  • 71
  • 105
Sree
  • 2,727
  • 3
  • 29
  • 47
  • 1
    Note that in RxJava 2 operators like `map` allow throwing of checked exceptions from the lambda. This was really a design flaw in RxJava 1 because the exact error thrown couldn't be propagated in 'onError` from the map lambda without being wrapped as a `RuntimeException`. – Dave Moten Dec 01 '16 at 20:23

4 Answers4

24

rx will always catch error, even if this is RuntimeException. So you can throw Some kind of Runtime exception in catch block. This is how actually it should work.

 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);
        }
}
Dave Moten
  • 11,957
  • 2
  • 40
  • 47
wnc_21
  • 1,751
  • 13
  • 20
  • 1
    when I try to do this, it asks me to surround the exception with a try-catch – Sree Nov 06 '15 at 23:27
  • 2
    You have to throw RuntimeException. JVM will not ask you to surround it with try catch. That's the idea – wnc_21 Nov 07 '15 at 20:09
  • I made `call()` throw an IOException but it is saying overridden method doesn't throw IOException – Sree Nov 09 '15 at 16:54
  • 3
    Come on. IOException is not runtime exception. Read my answer one more time. – wnc_21 Nov 09 '15 at 19:36
  • ah, sorry. makes total sense. can you look at @akarnokd 's answer above which uses `Observable.error`. Would you recommend your approach over that? – Sree Nov 09 '15 at 19:55
  • It depends on, what are you going to do. Observable.error() returns new Observable and will work only with flatMap If you want to have mObservable.map(something can trhow an error).filter(exception here).subscribe(...) in this case, it is not possible to use Observable.error() – wnc_21 Nov 09 '15 at 20:05
  • last question: so if I needed to handle an exception, I need to write a wrapper like that for each type of exception? – Sree Nov 09 '15 at 20:56
  • Only if you need it. You can throw RuntimeException or any class inherited from it. Wrapper adds additional sense. For example map() throws IOException and FileNotFondException and you need to handle them separately. In this case you need to have wrappers, to distinguish them in onError() – wnc_21 Nov 09 '15 at 21:25
8

With 1.0.15, there is the fromCallable factory method which let's you run a Callable instance for each subscriber where you can throw checked exceptions as well:

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

Edit:

source.flatMap(v -> {
    try {
        //...
        return Observable.just(result);
    } catch (Exception e) {
        return Observable.error(e);
    }
})
.subscribe(...);
falvojr
  • 3,060
  • 4
  • 31
  • 54
akarnokd
  • 69,132
  • 14
  • 157
  • 192
3

Just created helper class to extract this boilerplate to another place:

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;
    }
}

You can call it like this:

.map(RxRethrow.rethrow(products -> mapper.writer(schema).writeValueAsString(products)))
MercurieVV
  • 404
  • 6
  • 14
3

I don't know how the situation was when this question was first asked and answered, but RxJava currently contains a helper-method for this exact purpose: Exceptions.propagate(Throwable t)

RxJava Javadoc

Convenience method to throw a RuntimeException and Error directly or wrap any other exception type into a RuntimeException.

Thorbear
  • 2,303
  • 28
  • 23
  • @Sree Wow, I actually didn't spot that, I was blinded by the custom exception class. Now that I look closer I can see that there is actually another answer here using the same method. – Thorbear Feb 04 '17 at 10:18