(upbeat intro music)
So this part we are now ready to create
the ViewModel for our app.
We want to start here by adding the necessary dependency
to the build.grade file.
so lets open our build.grade file,
the module app, grade file is what we want.
And what we want to do is add those dependencies.
As we did for the Calculator app,
you can copy and paste those same entries
that you did last time.
We got those from one of the Android support pages.
I'm just gonna drop it in here.
So either way will work.
So def life cycle_version
is equal to point type a quote "1.1.1"
Then on the next line we're going to add a message there.
The comment should say ViewModel and LiveData
which is what this next line is going to be all about.
We'll type implementation and double quotes.
Android.arch, A R C H.lifecycle
colon extensions
colon and a dollar sign
it's gonna be lifecycle
in this version which is our variable,
we've defined in the previous line.
And closing double quote.
The other entry we needed to change,
which again we needed to do for the Calculator app,
is change J R E to J D K.
Let's go ahead and do that.
And at that point we can now sync.
It goes without saying, that it's important
to type this exactly correct.
If you don't type that line correctly you get an error
when we go to uh, to sync.
But in my case I've been able to sync it,
so we're good to go.
Alright, so let's have a think about
how this ViewModel will fit into the overall structure
of our app.
Hopefully by now you've reviewed the Google document
that we looked at in an earlier video.
There's actual a diagram showing the lifecycle
of a ViewModel in that.
Let's just bring that up on screen.
It is worth checking that out.
And we'll just come down here
to the lifecycle of a ViewModel,
click on that.
And there we can see the diagram that I'm talking about.
So that diagram shows the activity
being destroyed and recreated as a result
of a configuration change.
But the ViewModel exists
until the activity is finally destroyed.
If we make the ViewModel responsible
for downloading the data
then the data remains available
after our configuration change.
There's no need to download it again.
And in our case that's exactly what we want.
So let's actually see how a ViewModel
will fit into this app.
So as you can see in this slide,
the ViewModel is gonna be responsible
for keeping the feed data.
So exposes this said data, for a LiveData object.
Remember that LiveData can store any type of object
we've used simple Strings and Doubles in the Calculator app,
but here we're going to be using a LiveData object
to hold an ArrayList.
So when the ViewModel needs to get data from a new URL
it creates a new DownloadData object
and provides it with the URL
to download from.
So once DownloadData has finished downloading
it creates a ParseApplications object
to parse the data.
We've discussed the fact that this isn't the best design
and then we went and changed the way it worked
in the Flickr Browser app.
So it would be easy to separate these two classes
so the DownloadData sends the raw data
to the ViewModel.
Then the ViewModel uses ParseApplications
to preform the parsing.
But we're really focusing on the ViewModel at this point.
So we're not making any other changes at this time.
DownloadData then calls the Viewodel's
onDataAvailable function,
and passes the list of feeds as an argument.
The ViewModel now has an up to date list
of the feed data,
and exposes it through it's LiveData object.
That's "feedEntries" in this diagram
MainActivity observes this LiveData,
and can respond whenever it changes.
And in this case it responds by
updating it's applica- it's adapter rather,
with the new list.
Now we'll need to make a small change to the Adapter.
It'll need to provide a function
that MainActivity can call, to provide the adapter
with a new list.
You'll also need to notify the ListView
that its data has changed.
We'll make both these changes once we've written
our ViewModel class.
Alright, so that's how it all fits together.
So let's swing back now to Android Studio
and create a new class, in fact we'll create our ViewModel.
So I'm gonna go over, and under the top 10 download package,
new Kotlin File Class
and we're gonna call this one FeedViewModel
I'm going to select Kind of Class
started with a topping, press enter there.
And I'm gonna start off by adding the usual tag that we had.
That's private, const, val,
TAG = FeedViewModel
and for FeedViewModel we need to extend ViewModel
and we also need to implement the down loader
class is Downloader call back interface.
So let's do that.
So that's gonna be colon after the class definition.
And ViewModel Parenthesis, comma,
and download data,
.DownloaderCallBack
so I've got an error at the moment as usual
because we haven't yet, implemented
the interface function yet.
But we'll fix that in a moment.
First though we need to move some variables,
from MainActivity into here.
Let's go and check out MainActivity,
and I'll just close down some of these other classes
or at least the greater file of it.
So I'm looking at our MainActivity class
to finish it down here.
We want the DownloadData field,
that's obvious because MainActivity
is no longer responsible for downloading.
But we also want the feedChachedUrl variable
our ViewModel will decide if the feed needs
to be downloaded again.
All MainActivity is going to do is request the feed.
So let's actually take a copy of this
select all of the feedCachedUrl
I'm gonna cut that and paste that
after the DownloadData definition
so I can actually copy both of those lines.
And before I do that though, you might be tempted
to move feedUrl and feedLimit into the ViewModel as well.
In this case that's not a good idea.
The URL that the user's selected
is part of the interaction with the UI
so our responsibility for building the URL
belongs to MainActivity.
So I'm gonna copy those up,
or probably should save those two lines
that's the private var downloadData and the freeCachedUrl
cut those out of MainActivity
and go back to our FeedViewModel
and paste them in there within our class definition.
And looking back at MainActivity,
we've now got arrows there or more arrows.
But don't worry, that'll become right
as we work on converting the app to user ViewModel.
Alright so back to FeedViewModel
we've pasted those two entries in there.
So the ViewModel has to expose its data,
and we're gonna use the same technique that we used
at the end of the calculator conversion.
So I have a private MutableLiveData object
and expose the LiveData, to the ViewModel's own.
Let's go ahead and do that.
So it's going to be private,
val feed equals
it's gonna be MutableLiveData
and the type there is going to be,
a list of our FeedEntry objects.
To list FeedEntry, I need to have parenthesis
on the end of that.
And on the next line, we're going to define
our LiveData objects.
So val feedEntries,
colon LiveData
and the definition, begin as a list of FeedEntry objects.
List of FeedEntry.
Then we need to find our get function,
so get = feed.
FeedViewModel will update feed,
and MainActivity will be observing feedEntries.
Now there's a minor problem with LiveData,
it's not one I can see Google changing,
at least not in the short term anyway.
We've declared our LiveData and MutableLiveData objects,
as storing a list of feedEntry objects.
Unfortunately though, that will hold the server till now
until we initialise it with something,
there's not really anything else we can return.
So we're gonna make sure we don't get any errors,
as a result of returning now,
by initialising the MutableLiveData object straight away.
So I'm gonna add a nit block to actually do that.
So I'll add that just below the definitions there in it.
Left to right curly braces, and we're gonna type
feed.post value,
then in parenthesis, EMPTY capitalised
_FEED, _LIST.
Closing in our right parenthesis there.
So that's worth doing, but you'll still see the
compiler complaining, we'll deal with that
when we come to it.
But what's this EMPTY_FEED_LIST?
Well the Java Collections class provides
and empty list for situations like this.
Rather than creating a new ArrayList
we can actually use the empty list
that Collections provides for us.
That's a bit more efficient than creating objects
that we are not going to use.
Another advantage is this empty list is immutable.
Now if for some strange reason,
our code tries to add items to it
we'd get an error.
So you see the empty list from the Collections class
makes that code a bit more robust.
You can't actually mark EMPTY_FEED_LIST as a constant,
but that's effectively what it is here.
What we can do though
is put the definition at the top of the file,
so that it's available in other parts of the project.
Let's go ahead and do that.
So we're gonna do that just below
the TAG definition online, its 11.
So that's gonna be val, and in uppercase EMPTY_FEED_LIST.
And it's a list of FeedEntry objects,
so list FeedEntry objects, and it's gonna be equal
to Collections.emptyList.
You can see the arrow disappears on line 26.
And just out of interest, if you click on this emptyList
method call there, because it's Java source code.
You'll see that it's a top side version
of the empty, and it's called list constant.
So it's been cast to top we want,
which in this case is a list of FeedEntry objects.
Just above the doc stream for the method,
there's a definition of EMPTY underscore LIST there.
You can see there on line 4,443.
And that's effectively the object
that we're gonna be getting here.
That's a public static final object
which is the Java equivalent of a public const in Kotlin.
Now let's just close down that Java source.
Back to our FeedViewModel.
So the next bit of our code, we need to list
our MainActivity as the download URL function.
So let's go and grab that.
So I'm going to cut that out of the MainActivity class
and we'll paste that directly in to our FeedViewModel.
We'll paste it right in there below the init.
Now this situation, I'm gonna suggest you accept the imports
even though you can see that one of them there
is a synthetic import.
We'll fix that next.
Let's clean up some of these empty spaces.
So we need to make a few changes to this function.
Now the function was private in MainActivity.
But we need to make it available
to our ViewModel's owner.
And also take the opportunity
to where the logging line one's called.
And the last thing we want to do is make a change
to remove the X in the list view
from the DownloadData constructor.
So let's remove the private there,
so the ViewModel owner can access this.
Gonna add some logging as I mentioned.
Log.D parenthesis, TAG, comma
We'll just put downloadUrl,
and called with url $ feedUrl.
And as I mentioned we need to remove the second argument
from the DownloadData constructor.
And you will often have to tweak functions slightly,
like we're doing here when you move them into a ViewModel.
The changes we make here are quite trivial.
The only ones we had to make,
were to remove private and delete the reference
to the ListView.
We checked the X in the ListView,
that's a UI component,
and that doesn't belong to the ViewModel.
So Android Studio should have removed that synthetic import
but if you go up and have a look there
if not you can delete it now
and you can see we also got the X in the ListView there.
They're greyed out now, because they are no longer in use.
I'm going to remove those manually.
But you might find Android Studio's done
that for you automatically.
So the DownloadUrl function at this point
is doing exactly what it did before
including checking, to see if it's necessary
to download the dollar again.
If the URL hasn't changed, then there's no need
to perform another download.
However we did provide the users with a refresh menu item
and we need some way to indicate
that the download should be performed again.
And we can do that by invalidating the FeedCacheUrl.
So to do that, I'm gonna add a function
after downloadUrl.
So fun invalidate we'll call it, parenthesis
and we'll just type feedCacheUrl
and set that equal to "INVALIDATE".
Below that,let's actually get Android Studio
to generate the required onDataAvailable function
for our interface.
OnDataAvailable.
And let's actually add some Logging first
our Log.d will override the to do.
TAG, onDataAvailable called,
then we'll type feed.value = data.
Valueing what's past of this function.
We'll do Log.d parenthesis, TAG, comma,
onDataAvailable ends.
So our only work down there is
to assign the data to the MutableLiveData object.
So the final change now is
to do something that MainActivity's currently doing.
We've still got a few arrows in MainActivity
you can see here.
And we're gonna fix those in the next video,
when we get this modified app up and actually running.
And there aren't really many changes to make
as you can see there.
But one that we will make right now
is right at the end of MainActivity.
If you go right down to the bottom there,
we got this onDestroy function.
So this onDestroy function's cancelling
any download that may be going on.
MainActivity no longer knows about downloading.
So therefore it shouldn't be tempting to cancel it.
We can delete that function from here.
So we're going to do that.
Now this is an extremely important change
it really highlights one of the advantages
of using a ViewModel.
With this function in MainActivity,
consider what happens if the user starts a download
then rotates the device.
When the activity gets destroyed,
this function will cancel the download.
It may have nearly finished, but it will still be cancelled.
When the activity is recreated,
the download will start again.
But now that we've deleted that function,
instead will cancel any pending download
in the ViewModel using its onCleared function.
And I'll get Android Studio to generate the function.
Just go back to that now.
So implement the onCleared function.
You can see that's on the ViewModel there.
And we'll start by removing the super,
with a Log.d, parenthesis, TAG comma,
onCleared colon
cancelling pending downloads.
Then we'll put downloadData code operator .cancel
parenthesis true.
So remember from the ViewModel lifecycle,
that onCleared's called
when the ViewModel's no longer used.
It's a good place to tidy up any resources,
such as any acing tasks, for example that is selected.
And because the ViewModel survives the configuration change,
the download won't be cancelled
when the user rotates the device.
That's a huge improvement.
Alright so that's our ViewModel finished.
The next video we'll start the process
of modifying MainActivity, and run the app
and check out what we need to do to finish it off.
So I'll see you in the next video.