My solution was to use RxJava Observable.timer(long delay, java.util.concurrent.TimeUnit unit) method.
I declare a RxJava Subscription field, and then init it to an Observable.timer(20, TimeUnit.SECONDS) call, just before any Parse.InBackground() code.
Inside the Observable.timer() method, I invoke another method that'll throw a Java Exception within a try{...} block, and then handle the thrown exception within the following catch {...} block. What this does is have the Observable.timer() call invoke the exception-throwing method as soon as the set time (e.g. 20 seconds in the example above) is exhausted. By handling it in the catch {...} block, you can show a dialog/alert informing user that the operation has timed out.
Here's a code snippet showing this:
Subscription timeoutWatcher;
public void loginWithEmailAndPassword(@NonNull String email, @NonNull String password) {
timeoutWatcher = Observable.timer(10, TimeUnit.SECONDS).subscribe(aLong -> {
// Login timed out. Notify user to check Internet connection is chooppy.
throwNetworkException("Timeout! Your Internet connection is unstable and preventing your sign in from completing on time. Please try again.");
});
ParseUser.logInInBackground(email, password, (user, e) -> {
// if Parse operation completes before timeout, then unsubscribe from the Observable.timer() operation
timeoutWatcher.unsubscribe();
if (user != null) {
// Hooray! The user is logged in.
} else {
// Signup failed. Look at the ParseException to see what happened.
}
});
}
}
private void throwNetworkException(String exceptionMessage) {
try {
throw new NetworkErrorException(exceptionMessage);
} catch (NetworkErrorException e) {
// show alert dialog
}
}
Not the neatest piece of code, but it works for me.