G'day everyone, welcome back.
At the end of the last video, we were able to add new tasks the database,
but the RecyclerView wasn't updating to display them.
We had to close the app and run it again to see the new tasks.
That's not exactly the ideal user experience.
Fortunately, all the support to make the RecyclerView update is built into the Android framework.
So the changes we need to make to our ContentProvider are really simple.
Where it gets a bit more complex is getting the ViewModel to react to the notifications that the ContentProvider will issue.
With the CursorLoader all this was handled for us.
Now we have to do it ourselves.
One thing you may be asking is, why does he keep talking about loaders when they've been deprecated.
The answer is simple.
If loaders are deprecated, they're going to be removed from Android at some point over the next couple of years.
But before that happens,
companies that have apps that used loaders
are going to be looking for programmers that can make them work without the loaders.
After completing this section, you'll be one of those programmers.
Okay there are two parts to this. The first step is something that I've hinted at a few times.
We need to modify our ContentProvider, so that it notifies any observers about changes to the data.
Our ContentProvider has three functions that can result in changes to the data;
the insert, update and delete functions.
The code we need to add is very similar for all three, so let's start with the insert function in the AppProvider.
Open our AppProvider, scroll down a bit, and the change is simple.
If something was inserted then the record ID will be non-zero.
Let's log that we're starting,
and then we'll call the notifyChange function
with the URI of the value that has changed.
Notice that we're passing the original URI that was provided to the insert function.
The URI that the function returns will have the new ID appended to it
and that refers to a specific row in the table.
It is the URI for the entire table that we want to notify changes on.
I used the safe call operator when getting the ContentResolver from the context.
We know the context won't be null, but using the safe call operator removes warnings.
We do something very similar for the update function.
Scroll down a little way further, and once again check that the count is greater than zero.
That makes sure something happened.
Log that we've started, and then call the ContextResolver to notify the change,
again, passing the URI.
The final change is to the delete function.
Once again, it's pretty much the same.
That's the first part done.
When we call a function of the ContentProvider that results in a change to the database,
the ContentResolver will notify any observers about the change.
The second part is to observe these notifications.
We do that in the TaskTimerViewModel class.
We need to create a ContentObserver and register it. Let's start by creating a ContentObserver.
If you're prompted for the correct import in the handler, we want android.os.handler.
When a change is notified, the onChange function will be called.
We log the fact, then we call loadTasks to reload the data, and update the current cursor LiveData.
The next step is to register the observer, and the init code is a good place to do that.
We tell it the URI that we want to observe changes on
and provide our ContentObserver.
In an app with many tables that get updated, we can create an observer for each URI,
or we could register the same observer for several URIs.
Notice that the URI is passed into the onChange function on line 22,
so we could respond differently to different URIs.
When you register an observer like this, it's important to unregister it again.
If you don't, you can end up with memory leaks.
Remember from our discussion on ViewModels, that they have an onCleared function.
That function gets called when the ViewModel's no longer used and can be destroyed.
It's a good place to clear up any resources, and that's where we'll unregister our observer.
Android studio will generate the stub for us.
ctrl o, select onCleared
remove what's already there, and add our code.
Unregistering the observer is similar to registering it.
We call the ContentResolver's unregisterContentObserver function.
Let's see that running
We should be up to task 7
but we'll set the sort order to 2.
That way it should appear on the list before task 6.
I'll click Save, and when I return to the main screen there's our new task 7 showing in the list.
Let's have a look in the logcat,
and also see how to fix a quirk that it's got at the moment.
If you find your log cat running wild and logging all sorts of things,
even when you've got the show only selected application filter on the right,
don't panic.
Use the second drop down from the left and select your application again.
That causes it to respect the filter. It seems to be a quirk in Android Studio at the moment.
Ok, type the filter, query|insert, with the regex box checked.
That will make it easier to find what we want.
There's our insert happening, and just below that we can see the query being called.
So our ViewModel has re-queried the database to refresh its cursor.
It then updates the database cursor MutableLiveData object, which is being observed by the MainActivityFragment.
When the MainActivityFragment observes that the cursor has changed,
it swaps the adapter's cursor and the RecyclerView can immediately access the new data.
There are two important changes we had to make, to make all that happen.
Firstly, we made sure that all our functions that can change the database; insert, delete and update,
in the AppProvider,
call the ContentResolver's notifyChange function.
And so they notify the listeners that there's been a change.
Then, we set up a ContentObserver in the ViewModel class,
and registered it to be notified of the changes.
Okay, we'll stop the video there.
In the next video, we'll work on the onClick listeners for the buttons,
so that we can then edit and delete tasks.
By the time we've done that, you should be able to tell me what we're doing wrong.
Our code works, but we're doing something that we really shouldn't be doing.
It's something we've seen mentioned in the documentation,
and we've hinted at it a couple of times.
We'll be putting it right in a future video, but until then, see if you can work out what it is.
See you in the next video