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.
Related
I have an android application where I have an activity with two fragments. One with a map view and the other with a listview. In the main activity I get the user's gps and results from the database based off the gps and send that data to the fragments. That made since instead of calling inside the fragment twice the same data. However, a lot of times the request for the backend data and the gps coordinates of the user are not completed before the fragments are created.
I set up an interface to pass the data back and forth but need to find a way to pause the fragment creation until all data is there.
Any ideas? I can post code if neccesary.
Don't "pause" the fragment. Have a "loading" state, a "loaded" state, and an "error" state. Then show the user what is going on between each state.
You should not block the fragment UI until the data is ready, but instead you should show the user that the page is loading (e.g. with a Progress Bar).
Once the data is ready, you should populate the fragments with data (through a callback) or show an error message.
Thanks for the responses. I found a better way to do it. So I was passing data from the activity to fragment via an Interface and in the onAttach method of the fragment. Instead I created a Local Broadcast Manager that listens for the data to be complete in the activity class and sends to the map fragment to populate.
Hope this helps someone else.
I have an App with 2 activities A -> B. From B the user can update data being displayed by A, so I want A to show fresh data when the user navigates back (actually not so simple, some network involved so data can be updated at any time).
The activities are listening to ddbb changes so the views can be updated when data changes. Problem is data can change while activities are in background, so I am not sure when and how i should listen for changes. I can think of two approaches:
Listen for changes during the whole Activity lifecycle (onCreate - onDestroy) and just update the views when the data changes. This will update views from background activities.
Listen for changes only when the Activity is being active or displayed (between onStart/onStop or onResume/onPause) and force a view update every time the activity comes to the foreground (since changes might have happened while activity wasn't listening).
Mixed approaches; keep the listeners the whole lifecycle but only update views when activity comes to foreground.
Im not sure which is the correct way to handle data observing while in background.
Option 2 sounds more reasonable, but having to update the views when the activity comes to foreground may lead to UI lag right when the user starts interacting with the activity.
Option 1 will cause a lot of updates every time data is updated.
Thoughts on this?
You can choose any of the three options that suits your particular use case. Though I will say that the conventional use is with start/stop. onStart says that the activity is visible to the user in some way (either fully or partially) and onStop says that the activity is definitely not visible. So if you don't care to update the UI while the activity is not showing at all, use these lifecycle states.
You just may need to be prepared to capture data that you may have missed while the activity was stopped before it gets started again (for example, if the user pressed the home button, then came back to the activity via the task switcher. So performing your query again and rebuilding the contents of views from scratch may be necessary.
I have a Fragment in an Activity. When the Activity gets created, it triggers an asynchroneous load of the application state from a file. When this finishes, the Activity starts creating some paged Fragments, depending on the configuration state.
The problem is that when the OS kills the app, so that onCreate(bundle) has a stored state in the bundle because the OS took care of storing it prior to killing the app, the Fragments are also recreated immediately, but the application state is still getting read from the file, so they don't have access to the data they should be showing, which they need in their onCreateView(). This is because this time the Fragments are created by the OS, and not by the callback of the AsyncTask in the Activity's callback.
I'm hesitant towards making onCreateView() block/wait for the async result (even if it is avaliable in a fraction of second), so I'm thinking about moving the code which depends on the application state out of the Fragment's onCreateView(), and have some callback create it when the data is avaliable.
Ideally I'd work with deferreds/promises/futures, where I can "subscribe" to the async load result in the onCreateView() , but i'm too new to Java in order to know how to do this. I don't know which libraries exists or are suited for this.
Which suggestions do you have for me so I can deal in the best possible way with this issue?
Kind regards.
I am having an issue in ViewPagerwith Tabshaving Fragments. I have my Home Activity that has a ViewPager with Tabs. For each Tab there is a Fragment. There are 3 sets of data coming from service when we start the application. The requirement is when I do changes for these sets of data in some other activities, I should be able to see the changes in the Home Activity, that means the service calls are to be done again in each of these Fragments, which is why I am doing all this part in onResume().
Required Output is :
When coming back to HomeActivity(that has the tabs) from another child activity, the service calls should be done.
When hitting on Load More button in any of the tab, the service call associated with that fragment or Tab should be done.
When moving between the Tabs, service calls should not be done, instead the data has to be loaded from Database.
Current Output is :
Since I kept all the service calls for those Tabs in onResume() of the associated Fragment, the first and second requirement is met.
When it comes to third requirement, the output is same as the first one i.e., its making service calls which should not happen.
Problem : How to identify if user is moving between the Tabs or if he is coming from child activity. This service calls happening while moving between Tabs is causing performance issues. Please help me with this, I have no idea about how to proceed to overcome this performance issue. Thanks.
Whats the recommaned approach to notifying the hosting activity of a fragment that performs some background processing, that its done. Assuming that the fragments are running some threads that are performing work outside of the main looper.
A simple callback won't do since:
The Activity could be detached due to screen rotation which would lead into a NullPointerException.
Polling from within the activity is just dumb
Only calling the activity once if attached and let the activity check after each onCreate call (i.e. due to screen rotation).
What I currently do but it seems wrong: Whenever the Fragment gets attached, it will check if the work is done and notify the activity via callback. If the fragment finishes the work it will also callback the activity (if attached).
Why I think is is wrong? Because I have some really ugly methods that check if the fragment is attached and also if the work is done in order to maybe call the callback. This becomes very stupid when an exception is raised during performing some work in the fragment and the activity is detached. If android decides to call onSaveInstance in the same moment I will have to put the Exception into the Bundle and deliver it later when the Activity and fragment is recreated from the saved state. Additionally I can run into a situation where a activity will received the same callback twice (once from checking the fragment and the second time when the fragments gets attached; this could happen when the application got saved and restored)
This generates so much code that, in my optinion, could be much more clear if activites won't get detached. That is why I hope I'm doing something wrong and hope someone will provide me with a better solution.
I would say you should use a service for your background processing but if you've chosen a fragment for a specific reason you can stick with that. The way you should notify the activity from either a fragment or a service that might have a different lifecycle from the activity would be through a BroadcastReceiver. With this method the activity can register and unregister a BroadcastReceiver during its own lifecycle callback. From the fragment or service you just send the broadcast and forget about it. If an activity is listening for the broadcast it will receive it, if not nothing will happen.