To better understand RxAndroid I found this Repo which is full of useful examples using RxAndroid, especially in combination with Retrofit.
So if I look at this part of the repo, I can make an http call by clicking an button which seems to be running in the background right?
What if I have this app which needs to show an activity/fragment and at the same time do some http call on the background and show the data if there is any received?
So for instance I have this fragment with onStart
#Override
public void onStart() {
super.onStart();
pomagistoService
.getAgenda()
.subscribeOn(Schedulers.newThread()) // <- run in background right?
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Appointment>>() {
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
#Override
public void onNext(List<Appointment> appointments) {
// show data in ListView
}
});
}
If I start this fragment a ListView instantaneously contains the data which is received by the http call.
Now the question/wondering I have is:
Does this http call run in the background?
I am asking this because of the data which is there immediately when the fragment appears, so I can't really observe it.
Usually, for network request you should use not Schedulers.newThread(), but Schedulers.io(). Also, to get some delay before request is executed and get a chance to see fragment without data try to use .delay(5, TimeUnit.SECONDS) to make 5 seconds delay before call.
if you're using subscribeOn(Schedulers.io()) then you're telling RxJava to do its job in background (another thread).
Schedulers.io() is usaually used for I/O operations like networking, you can use Schedulers.computation() for long processing operations running locally.
and by using observeOn(AndroidSchedulers.mainThread()) you're telling that you want to recieve the final result in the main thread so you can show it or handle ui events.
some common error is the confusion when using subscribeOn() and observeOn(), so to make it easy for you .. you're always want to observe results on main thread to reflect it in UI otherwise use what fit your case.
Related
I am trying to understand what will happen in case my activity isnt in scope when I get response for the retrofit query. I have a list of items in my activity, and I make a request to server and on response, update a few elements in the list. Below is the code.
I have the below retrofit dependency
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
Call<Output> responseCall = APIInterface.getServerRequest(input);
responseCall.enqueue(new Callback<Output>() {
#Override
public void onResponse(Call<Output> call, Response<Output> response) {
//update the data used by adapter.
adapter.notifyDataSetChanged();
}
#Override
public void onFailure(Call<Output> call, Throwable t) {
//nothing much to do.
}
});
I have a few questions.
When the onResponse/onFailure methods are called, are they called in the main UI thread, or are they still in the background thread?
What will happen if my user has moved out of the existing activity, say they moved on to the next activity, or moved back to previous activity, or closed the app altogether. Will my onResponse still get called? And if my activity isnt on the stack and I refer to some variables in there, will I get nullPointerException?
Is it possible, in the response method, to somehow get the reference to latest activity and then call the notifyDataSetChanged on the (maybe) new instance of activity?
enqueue will do the request on a background thread but the callback will be called on the main thread.
The request can return to an unavailable Activity/Fragment there are a few ways to mitigate problems.
If the call is returning to an Activity you need to check if the Activity is still running. There are a few solutions here. Android: how do I check if activity is running?
If the call is returning to a Fragment you can call isAdded() to check if you can mess with the Fragment.
Not cleanly. But that shouldn't be a thing you want. If you want the request to end up in the new Activity then make the request in that Activity.
Here are a few resources:
If you want to set up a cancelable callback that will not return an output if its canceled.
How to Cancel Retrofit request
If want the request to be lifecycle aware you use LiveData<Response<Output>> from your query. What are the benefits of returning a live data of a retrofit response
Lets say i'm developing a mobile application. As we know, by default, most of the code is going to run on the UI thread.
I want my application to run as smoothly as possible for my users, so i make sure that I am using async/await in all possible places.
On slower android devices, I am still having issues with Logcat telling me that I am skipping frames. So my next thought is to make sure that everything that can be done on a background thread, is put on a background thread. So besides work that has to be done on the UI thread, i put all of my code inside tasks on background threads.
My question is, Can I theoretically put too much code onto background threads, where I am actually making my application slower due to having to move between background threads and the UI thread too much?
You can do that quickly and can still controllable using Observable.
Observable.just("")
.observeOn(AndroidSchedulers.mainThread())
.flatMap { //do UI here }
.observeOn(Schedulers.io())
.doOnNext{ //do Async here }
.subscribe()
You need to use for background execution. Use AsyncTask.
public class SyncDatabase extends AsyncTask<List<ProductModel>, Void, Integer> {
#Override
protected Integer doInBackground(List<ProductModel>[] lists) {
return YOUR_INT;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(Integer row) {
super.onPostExecute(row);
// This is mainThread
//you can do what ever you want example: textView.settext(row+"");
}
}
Now you call from main thread
new SyncDatabase().execute(YOUR_GIVEN_LIST);
Yes you can put lots of job in background thread. You can use android threadpool system for group by group thread execution
You can do this if you are confident enough to handle all the thread communication efficiently by yourself. I would suggest using react approach. There are different react libraries available now for Android.
In reactive systems, A full job is broken into several parts. Different workers called Actors perform a different part of the full job and when finishes forward the job to next worker. Channels are used to send and receive messages between workers.
I know how to save states and restore them, but I just get confused when I have to do work with the Web services and to update UI. For times I was using the AsyncTask but then I came to point where I loose my activity/fragment context for example when I rotate the device. So in this way, I am thinking how other apps are handling such situations.
If I use the IntentService and call my web service from there, then I came to think that for each web service I have to make IntentService differently, and update the UI of each activity and fragment I have to make the BroadcastReceiver for each activity and fragments.
So what is a good practice for calling web service from the activity and the fragments?
How can I Update UI when the service return arrives (or call next service based on first services results)?
If you want your data to be instantly available through configuration changes (which you do), then you probably want to use Loaders.
It gives the developer a mechanism of loading data asynchronously for an activity or fragment. Since loaders are specifically designed to solve the issue of async loading, one does not have to spend too much time designing async tasks to handle all different scenarios efficiently.
Good article about Loaders https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832
Try using retrofit. It's a great networking libraries for Android apps and it's easy to use.
The entire network call + JSON/XML parsing is completely handled by it (with help from Gson for JSON parsing). Documentation is great and the community is huge.
check out this sample.
I noticed a comment you made:
...and my webservices are soap and I cant change them
The way I'm currently calling my web service, which is also SOAP, is via an Intent. I do this by passing in the data that I'm submitting to the Web service with putExtra then receiving it on my WebService, as you probably do right now. I then get the result from that web call and process it inside an AsyncTask, the async task will then utilize EventBus to post to Results as needed which are received on my MainThread via ThreadMode.Main.
So with that said, I highly recommend the use of a library called EventBus from Greenrobot.
You greatly simplify communication between Activities and Fragments, You can get started immediately using a default EventBus instance available from anywhere in your code. For example, you can do the following.
EventBus.getDefault().post(new ModelForOtherActivityToSee(data));
In the model, you can include anything you want, and react accordingly when received.
The best part is that when received, EventBus handles how the data will be executed by either running ASYNC, MAIN, BACKGROUND
ASYNC - Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long-running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
Background - Subscribers will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread.
MAIN -Subscribers will be called in Android’s main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly (synchronously like described for ThreadMode.POSTING). Event handlers using this mode must return quickly to avoid blocking the main thread.
An example of receiving an event broadcasted from EventBus:
//ThreadMode can be ASYNC, MAIN, BACKGROUND
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ModelForOtherActivityToSee eventModel) {
/* Do something with eventModel received, this runs on UI thread */
};
Full example on how to use EventBus:
1 - Open your build.gradle for the app and set your dependency for EventBus:
dependencies { compile 'org.greenrobot:eventbus:3.0.0'}
2 - Create your first model to use in publishing an EventBus, I will use a very simplistic example of a model:
package com.myapp.models.eventbusmodels;
public final class EventBusMyModel {
private final String dataRaw
public EventBusMyModel(final String rawData) {
this.dataRaw = rawData;
}
public String getRawData() {
return this.dataRaw;
}
}
3 - Now all that's left is pushing out a broadcast by using from anywhere.
EventBus.post(new EventBusModel("My Data here"));
4 - To enable Activities/Fragments to receive events from EventBus you must attach and detach, this is what I mean. From inside an Activity on the onResume() and onStop() overrides:
public class SomeActivity {
#Override
protected void onResume() {
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
}
#Override
protected void onStop() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onStop();
}
}
5 - The final thing to do is receive that broadcast, you can receive it in Any Fragment, Activity, or in all your fragments/activities. Here's an example from inside the SomeActivity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void eventThisNameDoesNotMatter(final EventBusMyModel resultModel) {
String receivedData = resultModel.getRawData();
//Do whatever with receivedData. Since we are on ThreadMode.MAIN, this is on the UI thread.
}
I have the following interface:
interface Callback {
void onSuccess(Result result);
void onFailure(Throwable t);
}
and the method:
void doSomething(Callback callback);
doSomething performs an asynchronous call to a server. The problem is that this method could be called from different components into the Android app at the same time. Taking that in mind, in order to implement doSomething, how can I be sure to return the result to the right callback?
What is happening is that the stored callbacks into doSomething are being replaced with the new requests and the wrong callback is getting the results of other requests.
What are the best practices to solve this?
Im using the Parse SDK, and at times, after running a few queries one after the other, all calls to Parse just get blocked and I need to restart my app. ( Log: http://pastebin.com/qk6jvtBb )
Usually a single operations involves these FOUR things: Save Object; Make Query; Save Installation; Send Push (im gonna keep the code really specific)
...
pObject.saveEventually();
...
pQuery.getFirstInBackground(new GetCallback<ParseObject>()
...
pInstall.saveEventually(new SaveCallback()
...
pPush.sendInBackground(new SendCallback()
What I want to know is. Should I use sendInBackground for all or saveEventually for all or should I run each in a separate Runnable with its own Handler?
I would recommend if all of the operations are specific to one task, then put it all in an IntentService, do nothing in the background.
so your code would look like this
public class MyClass extends IntentService
{
...
#Override
protected void onHandleIntent(Intent mIntent)
{
try
{
pObject.save();
pQuery.getFirst();
pInstall.save()
pPush.send();
}
catch(ParseException e)
{
//handle errors here DEBUG
}
}
}
The benefit to doing this is that your parse operations are executed one at a time, sequentially, and on a background worker thread. You won't have to worry about four/five different parse sdk calls running at the same time.
Remember, put the IntentService in the manifest. Start the IntentService with an Intent. No need to call stopSelf() when you are finished with your parse sdk operations. You will have to bind the service to your activity, or send a broadcast to let the activity or fragment know that the operation is complete.