RxJava MissingBackpressureException not thrown - android

We are using a lot of RxJava1 code in our Android app. Recently, we started getting a lot of MissingBackpressureException thrown around. So I tried understanding the mechanism around backpressure better.
I'm able to get a backpressure exception to be thrown
BehaviorSubject<Integer> subject = BehaviorSubject.create();
subject
.observeOn(Schedulers.computation())
.subscribe(x -> {
try {
logger.info("got " + x);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
for (int i = 0; true; ++i) {
logger.info("sending " + i);
subject.onNext(i);
}
That's great, I get a MissingBackpressureException, but when I cause the subscribe action to never return, I no longer get MissingBackpressureException, so this code:
subject
.observeOn(Schedulers.computation())
.subscribe(x -> {
while(true) {
try {
logger.info("got " + x);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
So I got a few questions here:
Why am I not getting MissingBackpressureException thrown in the 2nd subscriber?
What happens to all the objects? I don't see the memory footprint getting larger, so I assume that they are thrown away? why?
When I try to do it with RxJava2 by adding toFlowable(BackpressureStrategy.ERROR) to the subscriber, I'm not getting an exception in either of the cases, what's happening here?
subject
.observeOn(Schedulers.computation())
.toFlowable(BackpressureStrategy.ERROR)
.subscribe(x -> {

Related

Catching all possible exceptions while using android videoview

I am using android videoview to display a loop of videos, as per our requirement the video loop should continue even if one of the videos gives an error.
To catch any exception, I have included the relevant code in a try-catch block as shown in the below code. However, while testing all scenarios, I gave the wrong path to the videoview.setVideopath() but the exception is not caught. I can see in the android studio console that it reports the data source not found error, but catch block does not catch the exception. I also tried implementing onerrorlistener, it is also not called when this happens.
Could you please help me, I am attaching the relevant code and exception log, many thanks for your help.
private void DisplayVideo_VideoView(){
try {
adplayer = (ResizableVideoView) findViewById(R.id.adplayer);
String MediaStorePath = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + "/Videos";
// String videoPath = MediaStorePath + "/" + Root2Util.Videopathlist.get(CurrentMediaIndex).getFileName();
String videoPath = MediaStorePath + "/1" + Root2Util.Videopathlist.get(CurrentMediaIndex).getFileName();
//adplayer.setVideoPath(videopath[CurrentMediaIndex]);
adplayer.setVideoPath(videoPath);
adplayer.changeVideoSize(Root2Util.SCREEN_WIDTH, Root2Util.SCREEN_HEIGHT);
adplayer.setVisibility(View.VISIBLE);
adplayer.start();
adplayer.setKeepScreenOn(true);
adplayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
CurrentMediaIndex++;
//mp.reset();
if (CurrentMediaIndex == Root2Util.Videopathlist.size()) {
CurrentMediaIndex = 0;
}
playMedia();
// ErrorHandlerAsyncTask ErrorTask=new ErrorHandlerAsyncTask();
// ErrorTask.execute((Object)getApplicationContext(),(Object)String.valueOf(what));
return false;
}
});
} catch(Exception e) {
ErrorHandlerAsyncTask ErrorTask=new ErrorHandlerAsyncTask();
ErrorTask.execute((Object)getApplicationContext(),(Object)e.getMessage());
}
Exception log from the console :
W/VideoView: Unable to open content: /storage/emulated/0/Download/Videos/1f0a9106d-d7d5-470c-
b287-3e3cad7d13fb.mp4
java.io.IOException: setDataSource failed.
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1091)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1065)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1019)
at android.widget.VideoView.openVideo(VideoView.java:352)
at android.widget.VideoView.access$2100(VideoView.java:72)
at android.widget.VideoView$7.surfaceCreated(VideoView.java:628)
at android.view.SurfaceView.updateWindow(SurfaceView.java:580)
at android.view.SurfaceView.setVisibility(SurfaceView.java:256)
at root2tech.cloudplayer.HomepageActivity.DisplayVideo_VideoView(HomepageActivity.java:728)
at root2tech.cloudplayer.HomepageActivity.playMedia(HomepageActivity.java:958)
at root2tech.cloudplayer.HomepageActivity.access$200(HomepageActivity.java:78)
at root2tech.cloudplayer.HomepageActivity$5.run(HomepageActivity.java:571)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:935)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:730)
So, I just faced a similar issue, I needed to use a VideoView to play the video from a URL without any knowledge of if the URL was a valid video or not. I understand why you're asking about catching every exception as well. My VideoView would print out an IOException and another which I can't quite remember at the moment of this answer. To fix this, I used a very similar code to yours but in a different order. The setOnErrorListener is what solved my issue for me but I placed my OnErrorListener directly after my VideoPlayer initialization and before the setVideoPath.
This works because the setVideoPath is where the errors are handled, unfortunately, VideoView will print these errors to the log but it will not throw anything to crash the app (which I don't agree with or like one bit). Because of this, your setOnErrorListener should at least go before you set the path or there will be nothing for it to catch as the error would have been thrown already (The errors aren't thrown on start oddly enough.
To apply my solution to your code, I would change it to this:
private void DisplayVideo_VideoView(){
//initialize adplayer
adplayer = (ResizableVideoView) findViewById(R.id.adplayer);
//begin listening for errors
adplayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
CurrentMediaIndex++;
if (CurrentMediaIndex == Root2Util.Videopathlist.size()) {
CurrentMediaIndex = 0;
}
playMedia();
return false;
}
});
//build variables for readability
String MediaStorePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + "/Videos";
String videoPath = MediaStorePath + "/1" + Root2Util.Videopathlist.get(CurrentMediaIndex).getFileName();
//set path - If there is an issue with videoPath, the error should be thrown here
adplayer.setVideoPath(videoPath);
//final adplayer customizations
adplayer.changeVideoSize(Root2Util.SCREEN_WIDTH, Root2Util.SCREEN_HEIGHT);
adplayer.setVisibility(View.VISIBLE);
//begin the adplayer
adplayer.start();
adplayer.setKeepScreenOn(true);
}
It is not a good style of code to catch a raw Exception, it is too broad to handle different error cases. However, for most of the IO operations IOException is the base exception, it is recommended to catch this.
As per java docs, we also need to consider IO operations can throw IOError. Error is not under the hierarchy of Exception, but both IOException and Error share Throwable as the base class. With this consideration, you can write your try-catch block as:
try {
// Your code
} catch (Throwable throwable) {
if (throwable instanceof IOException) {
// handle IOException here
} else if (throwable instanceof Error) {
if (t instanceof IOError) {
// handle IOError here
}
} else {
//This else will be reached only if you have any custom exceptions
}
}
For more readability of the code, you can use multiple catch blocks:
try {
// Your code
} catch (IOException ioException) {
// handle IOException here
} catch (IOError ioError) {
// handle IOError here
}
Also, you can add throws clause to your method if you want to handle these exceptions later:
public void displayVideo() throws Throwable {
// Your code here
}
Note: This is not the perfect solution to capture all exceptions for your scenario. But, since your code also includes IO operations on the file, hence have used the above examples to explain how you can possibly implement your exception handling.

How to implement infinite loop by RxJava?

I need to listen to a socket as long as the app is alive. We do it in this way in Java (infinite loop):
public void listen() throws Exception {
String msg;
while (isRunning) {
byte[] buf = new byte[5 * 1024]; // Size: 5K
DatagramPacket packet = new DatagramPacket(buf, buf.length);
// blocks until a packet is received
udpSocket.receive(packet);
msg = bytesToHexString(buf);
print("Message from " + packet.getAddress().getHostAddress() + ": " + msg);
pm.setNotification(msg);
}
}
I am able to run it in another thread by using RxJava2 (to prevent Network On Main Thread Exception in Android).
try {
udpClient = new UDPClientMulticast("232.17.29.10", 4444);
Disposable d1 = Single.just(udpClient)
.subscribeOn(Schedulers.io())
.map(client -> {
client.listen();
return true;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(isListening -> print("UDP Client is listening: " + isListening), t -> print("Error: " + t.getMessage()));
cd.add(d1);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Even though it is working, I think this is super ugly. Is there any Rx way that let me to simulate infinite loop?
Why use RxJava for this? There's no need to. Just start a thread. You're overcomplicating things by adding in Rx at all.

Android Background Runnable not working on older devices

I have the following running at startup inside of an IntentService
threadManager.postBackgroundRunnableDelayed(() -> {
try {
// Calling getToken and getId must be on separate thread. Otherwise, it blocks UI thread.
String token = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE);
String id = instanceID.getId();
Timber.d("id: " + id);
Timber.d("token: " + token);
lazyRestQueue.get().sendGcmToken(new GcmPushRequest(id, token), getSendGcmTokenCallback());
setupAnalytics(token);
} catch (IOException e) {
Timber.e(e, "Exception during registration");
Crashlytics.logException(e);
setHasToken(false);
} catch (RuntimeException e) {
// This will be thrown if Localytics was not properly setup
Timber.e(e, "Exception during registration");
}
});
When I run it on anything below API 18 it causes the app to close (not crash oddly, just close)
can someone give me advice on this? I know it probably has to do with the service or something not timing properly

How to handle errors in BlockingObservable without subscribe()?

for various reasons I have to block on observable like this
Foo foo = fooBarNetworkRequestObservable()
.toBlocking()
.single();
return foo;
But how do I handle errors now? Is is possible without subscribe(new Subscriber .. onError()?. I tried wrapping the code in try-catch but compiler complains that IOException is never thrown in coresponding try-catch block. Any solutions? Thanks
You are in blocking world so you need try-catch. Since the API can't throw checked exceptions, we wrap them into RuntimeException for you:
try {
source.toBlocking.single();
} catch (RuntimeException ex) {
if (ex.getCause() instanceof IOException) {
// handle IOException
} else {
throw ex; // something other happened
}
}

RxJava Zip Operator Error Handling with Iterator

Background
I have a process that uses RxJava to get data from different locations based on a list. Each item is got through a different method (all returning Observables.). Due to having N items to get the logic operator to use is zip with an iterator.
The Problem
The code below works as expected but it seems "wrong" that I need a try-catch block to catch the Exception that is thrown by getBigFoo() - that returns a FooNotFoundException. Do not the other error related operators cover this, such as onErrorResumeNext() and onErrorReturn()?
private Observable<Bar> processFoos(List<Foo> foos) {
List<Observable<? extends IBar>> observablesToZip = new ArrayList<>();
for(Foo foo : foos) {
switch (foo.getType()) {
case BIG_FOO :
try {
observablesToZip.add(getBigFoo(foo.getId()));
} catch (Exception exception) {
//do nothing - but this seems wrong
}
}
}
return Observable.zip(observablesToZip, results -> mergeFoosIntoBar(results));
}
Attempts Made
The attempt below doesn't seem to catch the Exception generated. I don't understand why as there are technically no upstream or downstream items in the sequence, so Observable.empty() should work?
private Observable<Bar> processFoos(List<Foo> foos) {
List<Observable<? extends IBar>> observablesToZip = new ArrayList<>();
for(Foo foo : foos) {
switch (foo.getType()) {
case BIG_FOO :
observablesToZip.add(getBigFoo(foo.getId().onErrorResumeNext(Observable.empty()));
}
}
return Observable.zip(observablesToZip, results -> mergeFoosIntoBar(results));
}
You may want to use defer. getBigFoo should not throw an exception but instead return an Observable in error. So defer may help you to fix it :
Observable<IBar> obs = Observable.defer(() -> {
try {
return getBigFoo(foo.getId());
} catch (Exception ex) {
return Observable.error(ex);
}
});
observablestoZip.add(obs);
#dwursteisen was on to the right answer, but wasn't quite there.
My issue was that I was throwing a new FooNotFoundException:
throw new FooNotFoundException()
But what I needed to do was:
return Observable.error(new FooNotFoundException());
Then in my Zip function:
observablesToZip.add(getBigFoo(foo.getId())).onExceptionResumeNext(Observable.just(null);
Using the above combination means that the overall sequence does not abort and return an error when the individual Observables are resolved and potentially throw errors.
Could you make getBigFoo(foo.getId()) throw RuntimeException instead Exception?. All Exceptions on Pipeline must be captured, but not runtimeExceptions.
Take a look to this silly example
/**
* Here is a silly example how runtimeExceptions are not needed
*/
#Test
public void observableRuntimeException() {
Integer[] numbers = {0, 1, 2, 3, 4, 5};
Observable.from(numbers)
.doOnNext(number -> throwRuntimeException())
.doOnError(t -> System.out.println("Expecting illegal argument exception:" + t.getMessage()))
.subscribe();
}
private void throwRuntimeException() {
throw new RuntimeException();
}
you can see more examples here https://github.com/politrons/reactive

Categories

Resources