android.os.NetworkOnMainThreadException using rxjava on android - java

i'm having trouble implementing rxJava in order to check if there is internet connection on android i'm doing it like this:
on my launcher activity i have this in onCreate:
AndroidObservable.bindActivity(this,
Observable.just(Utils.isActiveInternetConnection(Launcher.this)))
.subscribeOn(Schedulers.newThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
i have an Utils class it is a final class with static methods the methods in that the observable is using are this ones:
public static boolean isActiveInternetConnection(Context context) {
if (isNetworkAvailable(context)) {
try {
HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
urlc.setRequestProperty("User-Agent", "Test");
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(1500);
urlc.connect();
return (urlc.getResponseCode() == 200);
} catch (IOException e) {
Log.e("network", "Error checking internet connection", e);
}
} else {
Log.d("network", "No network available!");
}
return false;
}
private static boolean isNetworkAvailable(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
return true;
}
else {
return false;
}
}
i'm receiving android.os.NetworkOnMainThreadException and i can't find why, thanks in advance.

Observable.just(...) is called immediately on the calling thread (the main thread, in this case). Your code is effectively just an inlined version of this:
boolean activeConn = Utils.isActiveInternetConnection(Launcher.this);
AndroidObservable.bindActivity(this,
Observable.just(activeConn))
.subscribeOn(...)
...
You've tried to move it off the main thread by calling subscribeOn() - but the call has already happened.
The way we handle this (and I'm not sure that this is the best way, but it works) is to defer the network or blocking call until subscription happens, set up the observable to run on the correct threads, and then subscribe:
AndroidObservable.bindActivity(this,
Observable.defer(new Func0<Boolean>() {
#Override
public Observable<Observable<Boolean>> call() {
return Observable.just(Utils.isActiveInternetConnection(Launcher.this));
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});

AdamS is correct, however RxJava 2 now offers Observable.fromCallable() to defer an observable operation till subscription.
A good reference:
https://caster.io/lessons/fromcallable-converting-slow-methods-into-an-observable/
Some example code from my use-case:
Single.fromCallable(new Callable<Response>() {
#Override
public Response call() throws Exception {
return NetworkGateway.networkRequest();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);

I guess just just calls the method synchronously as it expects the boolean value and it tries to get it.
I am rather bad at RxJava but you may try something like this:
Observable<Boolean> onlineObservable = Observable.create(new Observable.OnSubscribe<Boolean>() {
#Override
public void call(Subscriber subscriber) {
subscriber.onNext(Utils.isActiveInternetConnection(context));
}
});
onlineObservable.subscribeOn(Schedulers.newThread()).subscribe(result -> {...});

this is my retrieve data from DataBase by RXAndroid code:
Observable.create(new Observable.OnSubscribe<List<GRO_VO>>() {
#Override
public void call(Subscriber<? super List<GRO_VO>> subscriber) {
String jsonIn;
jsonIn =retrieveDataFromDB();
Gson gson = new Gson();
Type listType = new TypeToken<List<GRO_VO>>() {
}.getType();
eventJoinList = gson.fromJson(jsonIn, listType);
Log.d("RX",jsonIn);
subscriber.onNext(eventJoinList);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<GRO_VO>>() {
#Override
public void call(List<GRO_VO> eventJoinList) {
Log.d("RX", ".subscribe");
recyclerView.setAdapter(new EventJoinAdapter(eventJoinList));
}
});
I think the just operator will emit data immediately, so it's is not useful to retrieve data from a database via network. It is very easy to use, but it can only be used for data that's already in the ram of the device.
I also had that problem, like #Baniares had, but after I use the create operator, all the problem gone...
From the RXJava documentation:
static <T> Observable<T> create(Observable.OnSubscribe<T> f)
Returns an Observable that will execute the specified function when a Subscriber subscribes to it.
Using the create operator can establish the standard process:
1 .subscribe(...) Subscriber(the sub class of class Observer) start the connection to Observable.
2 .subscribeOn(Schedulers.io()) collect a backGround Thread from the RX-ThreadPool
3 .create(...) retrieve data from server...during some netWork..etc
4 .observeOn(AndroidSchedulers.mainThread()) this means data will be set by the UI-Thread
5 Once we get the data , we can set the data in the onNext( ) method in the .subscribe( ) , the data will be set on the UI by UI-Thread since we make UI-thread do the work on the .observerOn(AndroidSchedulers.mainThread())
And note that if you use .create( ) operator,you must finished your observable in the .create( ) , others operator such like map,flatMap will not be executed after the .create( ) operator.
A very important concept we must need to know before start to use RXJava/RXAndroid. RX is a callback-based library, you tell RX what it should to do in what condition, it invoke your pass-in function(or in JAVA I may should to call them anonymous inner class... ) to achieve what you want.

Related

Performing Callbacks one after another

I am new to doing asynchronous programming in Android Java. I am wondering if there is a way to run another Callback after an initial Callback function has completed. Right now, I think they are running in parallel even though the second relies on the first.
First Callback:
// GETTING USER
private interface FirestoreUserCallback {
void onCallback (User myUser);
}
private void getUser(final FirestoreUserCallback firestoreCallback) {
Task<DocumentSnapshot> task = fStore.collection("users").document(fAuth.getCurrentUser().getUid()).get();
task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
user = documentSnapshot.toObject(User.class);
firestoreCallback.onCallback(user);
Log.d(TAG, "user created");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "user creation failed");
}
});
}
Second Callback:
// GETTING ALL DOCUMENTS
private interface FirestoreDocumentCallback {
void onCallback (List<TableEntries> myEntries);
}
private void getDocuments (final FirestoreDocumentCallback firestoreDocumentCallback) {
fStore.collection("result")
.document(Integer.toString(user.getCompanyNumber())) // need to use User object returned from the first Callback
.collection("SAM").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<TableEntries> results = new ArrayList<>();
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
// add objects to results ArrayList ...
Log.d(TAG, document.getId() + " => " + document.getData());
}
firestoreDocumentCallback.onCallback(results);
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
onCreate:
getUser(new FirestoreUserCallback () {
#Override
public void onCallback(User myUser) {
user = myUser;
}
});
getDocuments(new FirestoreDocumentCallback() {
#Override
public void onCallback(List<TableEntries> myEntries) {
entries = myEntries;
}
});
getDocuments() relies on the user variable being given its value from the first Callback. I'm receiving this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
Callbacks are looking fine. You just need to check if your value is null or not before accessing it. Just add a null check
if(doubleValue!=null)
Using RxJava. First, we fetch the user and then fetch the documents. Rx-Java has an operator flatmap. flatmap is used to execute the sequential tasks, where the second task is dependent on the data from the first task.
final CompositeDisposable disposable = new CompositeDisposable();
//function to fetch user data
Single<User> getUser(){
return API.getUserData(...);
}
//function to fetch ducuments
Sinlge<UserDetail> getDocuments(int userId){
return API.getUserDetail(userId, ...);
}
//Subscribe
disposable.add(getUser()
.flatmap(user-> return getDocuments(...))
.subscribeOn(Scheduler.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObservable(){
#Override
public void onSuccess(UserDetail userDetail){
Log.v("Api result", "Successful";
//Do some work
}
#Override
public void onError(Throwable e)
Log.v("Api result", "Error Returned");
}
}));
If either of the API call fails, onError() is called. If first API fails, second API call is not executed and onError() is called.
The simplest solution for your use-case is to pass both queries to Tasks.whenAllSuccess() method, as explained in my answer from the following post:
Firestore - Merging two queries locally
So once the task is complete, you can use the elements from both queries. Another solution might be to use Android Jetpack with LiveData along with ViewModel, as the Android team recommends.

Not understanding RxJava MaybeObserver (chat notification not working)

I'm very new to Java, android and Rxjava. I recently noticed that in an existing project (not written by me) a chat notification that is supposed to be received isn't received. Thus I started to do some tracing. Below is part of the code.
Note: Notifications that do get received seems to always go to onSuccess in the file FCMServices
I've put breakpoints pretty much everywhere in the code below. What I noticed was for the notifications that I do not receive onSuccess and onError do not get called but onComplete does. However I find that strange as I thought either onSuccess or onError must be called before onComplete.
My understanding of those functions is based on http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html
//FCMService.java
currentConversationRepo.getCurrentConversation()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MaybeObserver<CurrentConversation>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
currentChatDisposable = d;
}
#Override
public void onSuccess(#NonNull CurrentConversation currentConversation) {
System.out.println("This is SUCCESS");
if (channelSid == null && author == null && usedAdId == null){
buildNotifyNotification(body, action, "", userId);
}
if (channelSid != null && author != null) {
if (!channelSid.equals(currentConversation.getChannelSid())) {
createChatNotification(author, channelSid, body);
}
}
currentChatDisposable.dispose();
}
#Override
public void onError(#NonNull Throwable e) {
System.out.println("Error getting current conversation: " + e.getMessage());
currentChatDisposable.dispose();
}
#Override
public void onComplete() {
System.out.println("This is onComplete");
currentChatDisposable.dispose();
}
});
I then started to do some tracing of where onComplete was called and appears that it was called by another onSuccess from the class TestObserver in reactivex.io
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/observers/TestObserver.html
//TestObserver.java
#Override
public void onSuccess(T value) {
onNext(value);
onComplete();
}
Which was in turn called by the onSuccess in MaybeFlatMapBiSelector class. (Also a reactivex.io class I believe)
//MaybeFlatMapBiSSelector.java
#Override
public void onSuccess(U value) {
T t = this.value;
this.value = null;
R r;
try {
r = ObjectHelper.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
actual.onError(ex);
return;
}
actual.onSuccess(r);
}
This turned out to be from the MaybeObserver interface
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html#onComplete--
My question is what exactly are the onSuccess of TestObserver and MaybeFlatMapBiSelector doing? And if it is even possible based on the information I have provided, why is it that some notifications goes to onComplete without going to onSuccess or onError in FCMServices.java
Have you tried to comment currentChatDisposable.dispose(); ? I've had the same issue not long ago where I was disposing of my disposable too early and no data where showing
Usually you call .dispose() when onPause() or onDestroy() of the lifecycle
PS: In case you didn't know Maybe in RxJava return either a single value, nothing at all or an exception.

Subscribe Conditionally to rxJava2 Observable

I have a rxJava2 Observable to which I want to subscribe conditionally.
The scenario i will make a network call and the subscribe() is called only when the device is connected to network.I want to do something like below
observable.
subsribeWhenConditionIsMet(
if (connected to internet) {
mPresentation.showNetworkError();
return;
}
subscribe();
}
)
Any suggestions on how to do this? Any better approaches available?
For now there is no such method is available in RxJava2. But if you are using kotlin you can do it using extension function. Declare it like below.
fun <T> Observable<T>.subscribeIf(predicate: () -> Boolean) {
if (predicate()) {
subscribe()
}
}
At the time of call :
anyObservable()
.subscribeIf { isConnectedToInternet() }
Extra
In case if you want to handle fallback situation you can write your extension like below and make fallback lambda optional so that we can omit it if not required.
fun <T> Observable<T>.subscribeIf(predicate: () -> Boolean, fallback: () -> Unit = {}) {
if (predicate()) {
subscribe()
} else {
fallback()
}
}
At the time of call:
anyObservable()
.subscribeIf(
predicate = { isConnectedToInternet() },
fallback = { showError() }
)
}
Note: You can also call it from Java, refer this link https://stackoverflow.com/a/28364983/3544839
Having internet access is more than a simple condition, if you think about it it's more like a stream of booleans, sometimes it's true, sometimes it's false.
What you want is to create an Observable that fires true when an internet connection becomes available.
If you're on Android, you can have a BehaviourSubject in a broadcast receiver
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
final ConnectivityManager connMgr = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
final android.net.NetworkInfo wifi = connMgr
.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
final android.net.NetworkInfo mobile = connMgr
.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean hasInternet = wifi.isAvailable() || mobile.isAvailable()
subject.onNext(hasInternet);
}
}
You still need to somehow pass the subject to your broadcast receiver but it shouldn't be a big deal.
Then, in order to subscribe to your observable only when this subject returns true, you can do it like so:
subject
.filter(hasInternet ->
hasInternet // Don't continue if hasInternet is false
)
.flatMap(o ->
yourObservable // At this point, return the observable cause we have internet
)
.subscribe() // subscribe

passing responses between observables in RxJava/RxAndroid

I have this code :
getLocationObservable() // ---> async operation that fetches the location.
// Once location is found(or failed to find) it sends it to this filter :
.filter(location -> { // ---> I want to use this location in the the onNext in the end
after finishing some calculation here, I either return 'true' and continue
to the next observable which is a Retrofit server call, or simply
return 'false' and quit.
})
.flatMap(location -> getRetrofitServerCallObservable( location )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Observer<MyCustomResponse>() {
#Override
public void onSubscribe(Disposable d) {
_disposable = d;
}
#Override
public void onNext(MyCustomResponse response) {
// I want to be able to use the `location` object here
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
I want to be able to use the location object from line 3(first observable), in the "onNext" that is trigerred by the second observable.
I can't manage to work it out.. any help would be much appreciated.
Instead of
getRetrofitServerCallObservable( location )
you could map the result to be a Pair (from your favourite library) of the response and the location:
getRetrofitServerCallObservable( location ).map(response -> Pair.create(location, response))
Then, in your onNext, you'd be receiving Pair<Location,MyCustomResponse> instances.
If you don't want to use a Pair class, you could use Object[], but if you do, please don't tell me about it :P

Equivalent of Java Interface callbacks in iOS Swift

I'm trying to find a way to handle asynchronous network requests in Swift. My plan is to isolate these calls in their own class, so I need a way to handle callbacks. In Android, I'm doing something like the following using an Interface:
apiService.fetch(data, new Callback() {
#Override
public void onResponse(Response r) {
// success
}
#Override
public void onFailure(Error e) {
// error
}
});
Can someone point me in the direction on how I can handle this similarly in Swift 3? Thanks!
It's possible to have multiple completion handlers. Here is a short example:
func request(url:String, success:#escaping(Any) -> Void, failure:#escaping(Any?) -> Void)
{
let task = URLSession.shared.dataTask(with: URL.init(string: url)!) { (data, response, error) in
if let responseError = error
{
failure(responseError)
}
else if let responseData = data //Make additional checks if there is an error
{
success(responseData) //Call when you are sure that there is no error
}
else
{
failure(nil)
}
}
task.resume()
}
Example of usage:
self.request(url: "https://jsonplaceholder.typicode.com/posts", success: { (data) in
//Do if success
}) { (error) in
//Do if error
}

Categories

Resources