How to pass new data to onCreateLoader - android

I have an Activity that implements LoaderManager.LoaderCallbacks<Cursor>. The CursorLoader feeds into a RecyclerView (think ListView). So inside onLoadFinished I swap the data inside my adapter.
Imagine the data has to do with stock performance for a day. Now I have a Spinner that allows the user to choose a day. And when the user changes day I want the data to change. So I am thinking that the Loader needs to be able to listen to the parameter. How do I do that? Per the documentation of getSupportLoaderManager().initLoader I am not sure if calling it inside my spinner’s onItemSelected will do that trick. Thanks for any help.

And when the user changes day I want the data to change. So I am
thinking that the Loader needs to be able to listen to the parameter
to me, it sounds like you want to requery your database again when those data change. you can use restartLoader. You have to possibility to provide a different id, which can be used as identifiers for the specific loader. From the documentaion:
Starts a new or restarts an existing Loader in this manager, registers
the callbacks to it, and (if the activity/fragment is currently
started) starts loading it.

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.

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

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.

Start loader from another loader. Right or Wrong?

I have listview that is populated through CursorLoader. CursorLoader is created by LoaderManager.LoaderCallback's createLoader method. I have no problems with this. The problem is in that I want to start another task when listview population is completed and fill listviews with additional data.
My current solution is to start a another loader inside onLoadFinished method.
Is this right solution or it can be done in more elegant and efficient way? Could you give some advice, because I don't have much experience in android development.
Thanks in advance.
Loading from onLoadFinished will working without a problem. I did something similar in a pet project I had. I loaded data from my own ContentProvider and from there loaded contacts data from the phone's Contact ContentProvider. Each entry in my db could reference multiple contacts so I had to load my item before I knew which contact info to load. I chained the init/restart LoaderManager call to when I first received my item data in onLoadFinished. It works just fine and I used the contact data as a list afterwards. Granted I didn't use this approach to load data into a view inside an existing list view item. I used the data inside its own list in a detail view for my item. It should still work with what you want to do, but it can get messy appending data to the views and whatnot, especially since the view "lifecycle" is outside your control.
A better approach, IMO, would be to code a custom CursorAdapter that would use an AsyncTask or AsyncQueryHandler to fetch the extra data as the views are being created. Make sure to cache the data for subsequent use as the list scrolls. This second approach has the benefit of being independent of the external/secondary loader. It encapsulates all the logic required to display the data you need which includes loading the missing parts. It keeps the view data and display logic cohesive, safely tucked away inside a reusable module.

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