Does Android development have any concept analogous to WPF data binding? - android

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.

Related

Android ViewModel inside RecyclerView Adapter for lazy database downloads

I have a question that is more related to proper design and architecture of MVVM than to coding itself. In my project I have a situation that on ViewModel is suplying data, that are later used in RecyclerView.Adapter to create a proper view.
However I wonder if this would be also correct (from proper 'way of doing things' POV) if some of the data would be supplied in form of id's to be further fetched from Room or external server? For instance during onBindViewHolder use some LiveData with observe() to update certain fields on succesfull load.
Doing data fetch in the views is a no-go. It defeats the very purpose of MVVM and in particular the Android Jetpack efforts. One of the big reason is the data needs to survive configurations. Putting "data fetching" mechanism in the view defeats that as the view can be destroyed and recreated anytime when need be.
So I would suggest you make sure all calls to the network or any other source of data for that matter revolve around the ViewModel and never a view. Let the VM feed the data to the View through observer.
Exception to what I have just said is such use case as loading images with Picasso or Glide where by you feed them URL and they load image. But that's a different thing as they are designed to handle that.
UPDATE with followup Questions
it's ok to put observe() still inside Adapter, during the binding process?
No! Data sent to the adapter must be complete in the purpose it is supposed to serve. For example, if you have to do list app and your Top-Level Activity displays all Todos, then you must feed adapter will complete data (Title, Created time, et al).
The data that are not going to be displayed (like descriptions or sub-to-do-lists) and aren't necessary to identify specific to do should not be fetched (unless you want to store them for sole purpose of avoiding a second network call and pass them to the next activity as serialized data).
When user clicks specific To-Do, then launch new activity with its own ViewModel that will fetch details for that activity (assuming you passed some ID with intent).
If the first, then I understand that observe() should not only update data, but also populate it later to Observer and call notifyDataSetChanged(), right?
Observe is a way to post data to the view when either it have changed or the old view was destroyed and so the new view is being given the same old data that survived the "view death". So it is in the same method where updating data of the Adapter should be done and hence call to notifyDataSetChanged() or similar methods.
I hope that makes it clear.
I think it's best to keep the ViewModel separate from the Adapter. From what I'm gathering you want to basically have each list item load it's own data from Room by having each list item observe on a particular ID for that item. This sounds rather inefficient as you're basically having each item execute a query on the database (or network call) to retrieve just one item for all items that are going to be displayed, think about how it will scale if you were displaying a 100 items. I think it's best that you just execute one query to get the list of data and set the data to the list items in the adapter, keep it simple. Note that onBindViewHolder() doesn't just get called once but multiple times when you're scrolling the screen, so it could get quite ugly if you're trying to lazily load every list item.

Best place to call RecyclerView.Adapter.notifyItem*?

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.

Pass a shared data structure to an Android Activity without Serialization/Intent

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

Multiple ArrayAdapter, single array

I have an ArrayList containing data for displaying. The content changes asynchronously.
I want to display the data in two Activities, both are using an ArrayAdapter (not the same class).
The problem is that ArrayAdapter provides synchronized access and notifications through add, insert and remove. So synchronized access is only possible through one and not two adapers.
The ArrayAdapter itself is not resusable since diffent views are used.
So the question is: what is the recommended architecture for having one ArrayList with multiple ArrayAdapter's?
Update
I would like to clarify. At the moment I have only one ArrayAdapter.
the data is stored in an ArrayList
a service is updating the data in the background via the ArrayAdapter
both Activity and service are accessing the ArrayList via the ArrayAdapter (multithreading synchronisation issues), but this is no issue because ArrayAdapter does the locking
Now I have another activity which should also display the same ArrayList and I don't know what to do. Clearly I need another ArrayAdapter, because the second activity has another layout. This means that two activities and a service are accessing the same ArrayList. The synchronisation of the ArrayAdapter is not sufficient any more, because the locking is in the ArrayAdapter, which means if service and activity 1 are using ArrayAdapter 1, ArrayAdapter 2 will still access and modify the ArrayList.
The content changes asynchronously.
I'm not sure I understand how you use the ArrayList between the two activities. (static field?)
The problem is that ArrayAdapter takes ownership for the array (it
duplicates it).
I don't believe it duplicates it. It stores a reference to it. (correct me if I am wrong)
When Activity2 is activated and creates ArrayAdapter2, the ArrayList
still belongs to ArrayAdapter1.
Both ArrayAdapters should have a reference to the same ArrayList at this point. This means that a change in the ArrayList would be reflected in both adapters.
Use one global array adapter for two activities and observe you async data.
It is not a problem. After changes in arrayList through one adapter, call notifyDataSetChanged() for the other one. That is all you need, IMHO. At least, it is enough for my ArrayList that is also in different Activity with its adapter. And I change it "by hand" and notify the adapter later, too.
The serious problem will be if you'll want to have one ListView with two adapters. But that is a senselees task, I think.
Updating the answer up to the updated question:
So, the problem is not in the two activities that are using the same arraylist by different ArrayAdapters, but in three components using possibly two different arrayadapters. But if you are using the second adapter only for displaying of the same list, as you are writing here, you needn't any additional synchronization at all. Simply call adapter2.notifyDataSetChanged() after every significant change.
The problem could arise only if you are doing simultaneous changes through two different adapters. Each wouldn't be notified in time on the changes made by the other one. As for synchronization between Activities in the case of probable simultaneous writing, you can reach it by notifying after every change focus from one Activity to another.
But writing synchronization between service+adapter1 and activity+adapter2 could be reached only if you'll find youself some important points in your code where the mutual notifications and locking/unlocking should be made.

Sharing data between services and activities

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.

Categories

Resources