I have a data load method that returns LiveData. This LiveData is then passed through the Repository and ViewModel and its contents are viewed in the fragment. This happens in the onCreateView method. The problem is that the data from the database is displayed on the screen after a split second and the effect of the data appearing after starting this window is created. How can you prevent such a delay?
It is necessary for the user to immediately see this data as if it was hardcoded on the screen.
#Transaction
#Query("SELECT * FROM data")
fun getData(): LiveData<List<Data>>?
Well that is how LiveData works. It releases your UI to continue rendering whats happening (loading the new fragment) and only updates the fields once data is loaded.
To stop this, you will have to slow down the UI transition by adding animations etc, but if a users phone is slow it might not be sufficient..
Alternatively you could design a loading screen and gracefully deal with the loading delay.
Related
I have an activity that in onCreate calls an API and populates a RecyclerView with product names, current stock, and an EditText to insert a value for each product.
You can then hit an add or subtract button at the top of the activity and all products with an inserted value will have their stock updated in the database by the given values via the API.
I do not pass the on screen stock values to the API (as another user may have changed it in the meantime), simply the inserted values, product ids and whether to increment/decrement the database value.
Currently, after I send the update request and it is successful, I call finish() and just reload the activity to get the updated data.
I'm wondering if it would be better to make an API call for the data after the update request and then just update the RecyclerView, it would almost be duplicate code of the onCreate but would prevent reloading the activity.
Does anyone have any insight as to the efficiency of this vs reloading the activity? It seems like reloading would be the more inefficient option but i'm not too sure.
Assume in your onCreate method you have something like this:
onCreate() {
setContentView(xx);
callApiGetDataAndSetDataToRecyclerView();
}
If so, pls try to change the code like below after I send the update request and it is successful:
doAfterUpdateRequestSuccessful() {
List<Object> latestData = callApiGetData() // this should not be in the ui thread, I believe you are clear about it but just want to repeat
recyclerViewAdapter.setData(latestData);
recyclerViewAdapter.notifyDataSetChanged();
}
Then your recycler view will be updated with latest data.
And like #ADM's comment, finish and recreate the activity is never a solution for updating recycler view, the ux will be very very bad.
Reload the activity is not so efficient. You should instead move your fetch data and render logic to an independent method and invoke this method from onCreate and every time you want to refresh the rendered data.
which is better between these two
1) Using coroutines in Viewmodel to fetch data from network and updating View using live data?
2) Using coroutine from View to call suspend function in viewmodel that fetches data from network?
Another question
Should we be using livedata for use cases where we need to update UI only once from backend, like data won't be changing while user is on that screen
I'm voting for (1), using LiveData for that final step of moving the data from the ViewModel to the View.
Here's why: if you start a coroutine in the UI which fetches the data through your ViewModel...
You'll end up with a suspending call like getData() in the View. Whether that's a Fragment or an Activity, that coroutine will deliver the result to only that specific instance. If it gets recreated due to a configuration change, you'll need to fetch again in the new instance.
If you're handling cancellation for your coroutines (which you probably should be), a configuration change will mean that any work you've already done in the ViewModel and around the network will be lost (e.g. any progress on a long running network call), since your coroutine is cancelled when your View is destroyed.
If you're not cancelling your coroutines when the View is destroyed, your data fetching function may attempt to update the UI in a View that doesn't exist any more if when completes.
In comparison, if you start coroutines in your ViewModel and then place the result in a LiveData:
Your fetches can continue across configuration changes due to the ViewModel's longer lifespan.
You can cancel coroutines when your screen is closed for good (in onCleared) instead of at configuration changes.
LiveData observers will only be called when the View exists and is in an active (foreground) state, so you don't have to worry about getting results when your View isn't ready for it (or doesn't exist anymore).
When your View is recreated, the new instance can start observing the LiveData and receive the value that's already loaded. Or, if your data is still loading, it will even eventually receive the result of the network call that was started for a previous View instance.
If I need to display data coming from an API in a Fragment (using an AsyncTask), let's say a list of items in a RecyclerView, I believe it shouldn't be done in onCreate() or onCreateView() since theoretically the view elements are being initialized and may not be ready to use if the call to the API is faster. Am I correct?
(I assume it's kind of impossible to get a response from an API in less time than it takes for Android to create the view though).
There is onActivityCreated() and onStart() but I am still confused about when the parent Activity calls them.
The thing I want to avoid is reloading data (making a call to the API) if it's not necessary, for instance because of an orientation change or going back to this Activity after a click on the back button from a possible "next" Activity.
Thanks.
If it is your first "window" (Activity or whatever) the only way I know is showing a loading text, image, etc.
If it isn't your first view you can load your data in another window and store it to later usage when the user reaches the View to show the info.
You can load it right in your onCreate, onStart or so, but as you said probably you won't have time to download the info, so again, show a loading page or whatever you want while your data comes.
To avoid initialization errors call your AsyncTask after initializing the elements. And to avoid calling the API multiple times save your data locally while the app is opened, it depends on your app requirements
I am having an app which is retrieving data in the main activity and sending an event to all fragments as soon as it is available. So for the first start it looks like this:
App starts (fragments are initialising in the background) -> feed download -> notification sent to fragments -> fragments initialise UI
Everything's fine so far. BUT, what if I am resuming the app. The data will be still cached, so i will send the event immediately on app resume, and therefore it can happen that my fragments are not even ready for receiving the event -> no fragment UI update!
Or the event is triggered and received in the fragment, but the fragment is not ready for the UI update, cause it still hasn't inflated the layout -> NullpointerException
Or the fragment receives the event, but is not attached to the activity anymore -> another Exception.
There are ways to deal with single issues, but overall it is complicating the architecture a lot.
Somehow I tried a lot of things (playing around with Otto bus) but somehow I can't find any architecture which is working for making a central datasource available to all activities and fragments in the app.
How do you supply your fragments with data if you don't want to use bundles?
First of all a Fragment should be independent from other parts of an app. Moreover it shouldn't know parent activity: getActivity method should return just an Activity which could be casted to some interface.
an Activity shouldn't be a "data downloader". Basically activity is a View which receives various system and user events and displays particular state. For instance when the system creates activity it calls method 'onCreate' where activity should create/arrange fragments and views.
there is should be some manager or controller(call it as you wish) which knows where and how to get data for views. For instance if there is no internet connection it loads data from local database otherwise it makes network request.
So roughly speaking flow should look like this:
fragment(or activity) has reference to the DataManager. The fragment subscribes on FeedDataEvent in the onResume method. When fragment wants(onResume method for example) to show some data to the user it calls DataManager.loadFeed() and displays to the user "loading..."
DataManager checks if there is Task which is loading data from network. If there is no such fast it starts it.
When data is downloaded DataManager emits FeedDataEvent.
If the fragment is still visible it receives that event and shows data. If the user left the app fragment unsubscribed(in the onStop method) from FeedEventData and will not receive that event.
There is subtle thing with requests caching(making network request on every onResume is not very good idea) but it depends on particular app.
PS Almost all this things are implemented in RoboSpice and some other libraries.
I am trying to understand some finer points of AsyncTaskLoaders. This may be obvious, to others but I can't find an unambiguous example or definition that demonstrates and exmplains what happens when you override the deliverResult() method. What actually gets delivered ? How does this interact with the calling object ? I can see use of super.deliverResult, which passes a private object from the class. So, does the loader automatically know what to associate with the "delivered result". I am totally confused.
Seems I'm a bit late to the party, but anyway...
One of the main advantages of this intermediary step between the background loading and the UI thread's callback onLoadFinished() getting called
loadInBackground()
deliverResult() and
the callback onLoadFinished()
is that it gives us a means of shortcutting the whole loading process from within the AsyncTaskLoader class.
And this can be put to good use for caching the loading result within your AsyncTaskLoader and preventing the background loading from happening if there is cached data.
And why would we want to do this? Isn't the whole point of loaders dealing with those dreaded activity lifecycle issues (e.g. rotating the device), maintaining state (like, caching data) and having a means to get updated when underlying data changes (CursorLoader)?
Well, yes, but this isn't the whole story.
Consider this use case:
You've got your app (the one with the AsynTaskLoader) up-and-running and it already has loaded data into your UI.
Then, you switch over to your Twitter app to check on some news and return to you app.
Without caching, upon returning to your app, the loader would do its reloading.
This behavior is different from the one after configuration changes, e.g. rotating your device, in which case no reloading would take place.
So, how would we then prevent the loader from re-fetching data in case we're just sending our app to the background and, later, return to it again?
Solution
Create a cache member variable in your AsyncTaskLoader implementation.
Override deliverResult() so that you save your fetched data in your cache first, before you call the superclass's implementation of deliverResult().
In onStartLoading() check if there's cached data, and if so, let your AsyncTaskLoader just deliver that. Otherwise, start loading.
Here's a link to a sample app which implements this behaviour.
It's just a "Toy app" and as such part of Udacity's current version of the "Developing Android Apps" fundamentals course. And here is the link to the respective video within that course that deals with this issue. (The course is free, but you'll still have to sign-up w/ Udacity).
In short, what this app demonstrates, is a UI in which the user can input a search query for searching GitHub's repos (via the GitHub API), showing the resulting search URL in a TextView and also the raw JSON fetched from GitHub in another TextView.
The whole action happens in just MainActivity.java and the relevant part here is within the AsyncTaskLoader that's implemented as an anonymous inner class:
For step 1, just introduce a member variable in your AsyncTaskLoader implementation that's meant to serve as your data cache.
/* This String will contain the raw JSON
from the results of our Github search */
String mGithubJson;
For step 2, override deliverResult() as to cache the loading result.
When loadInBackground() has finished, it passes its return value to deliverResult().
It does so anyway, but now that we've overridden deliverResult() we can step right in and store our fetched data into the cache member variable which we've created with just so good foresight.
And finally, we chain up to the super class implementation of deliverResult() with super.deliverResult() which will pass-on the result to the callback method onLoadFinished(), running on the UI thread.
#Override
public void deliverResult(String githubJson) {
mGithubJson = githubJson;
super.deliverResult(githubJson);
}
For step 3, check in onStartLoading() whether or not we've got cached data.
If we don't have cached data (yet), just force the loading to begin with a call to forceLoad().
But if we do have cached data, just call deliverResult(yourCachedDataGoesHere) and pass-in the cached data as argument.
if (mGithubJson != null) {
deliverResult(mGithubJson);
} else {
forceLoad();
}
So, if you now switch back and forth between your app and some other app(s), you'll notice that no reloading takes place, as the loader will just use your cached data.
suppose when data are loading in the background, at this time, user press HOME button and exist the app, when user comes back to the app, loading has been finished. So we have already have the data, then AsyncTaskLoader will call the deliverResult() method, deliver the data to the onLoadFinished() method for displaying.
When the user come back to app, onStartLoading() is being called before loadInBackground(). In this method, we could check if our data if empty or not, if not empty, we call deliverResult() and send the result to onLoaderFinished(), so it could prevent to reload data.
When we press HOME exist the app and then come back, it will not create a new Loader, instead the old loader will try to load data.
The only answer I can find that makes any sense is based on a decription in this link.
"A registered listener to receive the Loader's results when it
completes a load. For each of its Loaders, the LoaderManager
registers an OnLoadCompleteListener which will forward the Loader’s
delivered results to the client with a call to
onLoadFinished(Loader loader, D result). Loaders should deliver
results to these registered listeners with a call to
Loader#deliverResult(D result)."
deliverResult appears to be used when you have listeners to the AsyncTask and want to send the results back to them. I would say it's uncommon. The Android documentation is even less descriptive:
"Sends the result of the load to the registered listener. Should only
be called by subclasses. Must be called from the process's main
thread.
Parameters
data : the result of the load"
deliverResult works after doInbackground completes. It sends the result D (returned by doInBackground) to the calling thread. You may wish to override it for cleaning data, but you can do clean-up in doInBackground instead without overriding deliverResult.