I have a ListView that is displaying data from a large dataset. The data is retrieved from the web and put into a List<Data> (i.e. in memory) by a background task. (Think email inbox with polling updates.)
My problem is how to pass that data to the ListFragment/ListView/ListAdapter to display.
This List isn't permanent (so not in a Content Provider) but lives longer than activity (it's updated in the background when the activity isn't alive yet).
The only options appears to be:
Pass it via an Intent/Bundle. This requires serialization on some level which will be very expensive for my long list, especially as each time the List updated in the background, I have to set a new List which means the entire list gets re-serialized.
Create a Singleton that provides the list from anywhere in the program, and access SingletonListProvider.getInstance().getList() from my ListAdaptor. I don't like singletons and I'm worried about concurrency.
Use a ContentProvider. Seems overkill for a simple List<> that currently lives in memory
Are there any other options?
It seems I have little control over how the Activity (ListFragment in this case) is created so I can't just pass the List as a constructor parameter.
Best way is to use Database, especially if your data-structure large.
By the way, you shouldn't create ContentProvider enough to extend SQLiteOpenHelper.
http://www.vogella.com/articles/AndroidSQLite/article.html#sqliteoverview_sqliteopenhelper
Related
Updating ListViews with a CursorLoader was a simple way of displaying datas from a DB into UI. Model modifications where propagated to the UI with no extra work, and maybe not so efficiently.
RecyclerView.adapter, gives access to more granularity, allowing for instance to specify the adapter that a particular item was removed.
But, what is the best place to call those preferred methods (notifyItem*), replacing notifyDataSetChanged?
Obviously, the adapter must not observe the contentProvider, otherwise it won't know the nature of the model modification (just as before).
Different patterns could be used, like adding a bus to publish modifications from the provider, creating a singleton model which would hold a reference to the adapter, maybe using presenters (introduced in L), or creating an activity-bound service.
Here is a common use case: a sync process inserts an entry in DB (or a gcm notification is received, which also inserts an entry in DB), then i want UI (if launched) to be updated via a call to notifyItemInserted. Where to place this call?
Thanks.
I have a List of items that I want in a ListView, and I can make it work with setting a custom adapter every time the List grows, but the program flow is kind of weird and I have problems with persistence. (If I switch tabs, the UI gets rebuilt with an empty ListView.)
Now, in my day job I'm a C# developer, so when I look at this problem I see a WPF ListView bound to an ObservableCollection. Does Android/Java have something like that, a "fire and forget" connection between a UI element and a data structure?
You don't need to replace the adapter every time you change the data. The adapter "adapts" between data and view. There is no need to change the adapter as long as the way it adapts does not change.
Activity / Fragment lifecycle is not necessarily the lifecycle of your data collection. You can for example make a singleton data collection somewhere and use an adapter to display that collection all the time. Call .notifyDataSetChanged() on the adapter if you changed the data.
A persistent data collection in Android is probably best backed by a database. Take a look at LoaderManager & ContentProvider to provide and load data then displayed via CursorAdapter.
There is no automatic way of keeping a bunch of data available outside of your Activity / Fragment / .. lifecycle and it can get quite complicated but that's basically what you have to do if you want to keep data for longer than a given lifecycle. Singletons, Activity#onSaveInstanceState(), Activity#getLastNonConfigurationInstance(), Fragment#setRetainInstance(), ... are useful utilities to keep data in memory, databases are good for persistent data.
You have to do a little bit work yourself but it's possible. Use a ContentProvider as your DataSource. How the data is stored is up to you. I would prefer a SQLite-DB. A content provider has the possibility to add ContentObservers. (See this related question.)
You can write a CourserAdapter to fetch the Data from your content provider. And your ContentObserver should call notifyDataSetChanged() on your adapter. This closes the circle and your UI refreshes itself.
In Addition to zapls answer:
You can also write an adapter which contains a BroadcastReceiver. When your DataSource changes you can send a LocalBroadcast. The broadcast handler just calls notifyDataSetChanged() of your adapter. I think this would work around most of the lifecycle problems because only active elements will get the broadcast.
The google documentation has an example for such a solution.
I have one general question:
what do you think about following two alternatives considering style and speed
having a cached list in the main application and always using this list and load data on demand (and remove it on demand, for example if the cache get's to big)
always serialise the data, adding it to the intent, give it to the sub activity, working on the data, serialise it again and give it back to the parent activity and replace the original data in the list with the one gotten back from the sub activity
in my case, i've probably always a list with a thousand entries... and about 5 levels into the deep... always serialising costs time... and i've to handle the changes, because the parent activity newer knows, if the data was changed in the sub activity...
the global data cache has the advantage, that I always and everywhere interact with the same objects and never have to take care, that if data is changed, that I update this data somewhere else...
Is that a bad idea? if so, why? I want to speed up my application and am thinking about changing it to this model...
I have a ListView with a custom ArrayAdapter, that shows orders from an ArrayList. These orders are supposed to be synced with my server. There I have an API, which I am requesting for new orders, and if there are any, I get an XML of the order back. What is the best approach to dynamically update the listView? I do not want to use SQLite to store the orders, because only last ten are supposed to be displayed. With a ContentProvider I am not able to store my custom Order object. And if I wrap the ArrayList into a singleton class and use it in the service as well as in the Activity class for the ArrayAdapter, the ListView is not dynamically updated (probably, because the arrayAdapter makes a copy of the arraylist?). Thank you very much.
Filip
use Intent or Bundle
i'm no sure what you mean regarding the ArrayAdapter not being updated, but i can give you a solution we used in my company.
I have a DataMaanger which is a bridge between the Activities and the Networking or SQLite.
The dataMaanger keeps it's data in memory so it's not in DB or on disk. the disadvantage of it is if your app gets killed for lack of memory and reconstructs itself, the dataManager will be empty, which leaves you with two options, either on every Activitie's death or you main task's activities death you serialize your DataManager's data, or if you are not dependant on any previous data, just make arequest again and update the data manager.
I use broadcasts to notify my activities.
To get an access to the DataManager i don't use a sigletone. i use the Application object, you can extend it and in the Manifest.xml give it's name in the tag, then it will be used instead of the regualr Application object.
You can access it later by using getApplication() method in Activity class.
I have a similar issue with this one:
Android: Multiple activity instances launched by same intent. Bring one uniquely to foreground?
I need to create a stack of activities, all created by using the same class: it is a class defining a news list, only there needs to be multiple children activities that are also news lists, but from different categories. (I do need to have these activities in a stack)
The trouble is I need to change data on each of these activities after they are shown, but I can't find a way to access each one of these activities separately, since they are all using the same class, so if I used static methods, I would change the data on all these activities at the same time. Ideally, there could be a way to use references of each activity, so that I can access methods on each one separately, but I don't think there is a way of doing this.
I might as well pass parameter IDs when starting each activity, and instantiate objects at the same time, for each activity, and using these IDs later access the respective objects' methods...
Edit to clarify: Let me use an example to what I am trying to achieve. I have an A class and I am using this same class to instantiate multiple activities, in a stack. After the creation of these activities, I need to alter data, say, on one of these activities statically, so by calling A.alterData(); , but not when the activities are created, so there is no way of doing this by starting the activities with different data.. Since there are multiple instances of this class, if I do so, this will result on altering the data on all these activities, that are using the A class. Would I be able to somehow use objects and methods to these objects to alter data on different activities that are using the same class?
any other ideas?
You could use an ActivityGroup. It basically holds a list of activities and you need to control the navigation around them. It sounds like it suits your situation. There are many examples of them that can be found through google.
How I would approach changing the data on the other screens is by using shared preferences. You can store whatever data you need in there, and then (through your activity group) when you change screen, the data is refreshed. This is faster and a little more efficient than restarting the intent every time.
Another way is to change the data in the background without the user noticing. This can be done because an Activity group loads all of the Activity it holds and they are always there in the background, running, unless the developer states otherwise.
You could grab a a hold of the appropriate instance of the class you want to change the data on and then just change it.
Does any of this make sense?
I can elaborate more if needed.
I would supply the parameters to each activity, such as:
intent.putExtra("category", categoryId);
That way you aren't managing too much global state.
About changing the data - if you are talking about refreshing the data from its original source, then you should probably be doing this in the onResume() method of the Activity. Check out the Activity Lifecycle.
This has a few benefits:
you will have access to all of the context of that Activity
you won't have to do something nasty like access another Activity's data
you won't waste time refreshing data the user isn't looking at
Even if you have to make updates to the data, there are ways to make sure each Activity "minds its own business".