OK so we're gonna start with the basics with this application,
and the first thing we need to do before parsing the json data is to download it.
So I'm going to leave the user interface until later. To start with we'll download the data using an asynchronous
task, which is similar to what we did in the top 10 downloader app.
So let's start now, and we're going to come over here to our package name, we'll right click over here,
select new, Kotlin Class, and we're going to call this one GetRawData. To save a bit of time I'm going to choose
Class there in the drop down. We'll close down the log cat for now. So to start with we're going to extend the async
class, so that GetRawData can perform it's downloading on a separate background thread.
And again this is exactly the same as we did in the top 10 downloader app,
and once again we're providing a string,
or we're going to provide a string which will be the URL to download from,
and we'll get back a string containing our data.
So let's go ahead and do that. So I'm going to put a space colon space, and it's going to be an async task, and we want a
diamond operator so left and right, less than and greater than
signs there, then we're going to do a string, void, string, again you've seen that in the top 10 downloaders app, then
add parentheses on the end there. Now we've got an error, but before implementing the required methods,
I'm going to create an enum to hold a list of download statuses.
Now we saw this also in the previous app, when the Google YouTube player, to find an enum to hold the result
codes that we could get back if anything went wrong when playing the videos.
So here we want a way to tell the calling processes whether the download succeeded or what the state
of the download is. So I'm going to use an enum for that.
Now I have covered enums in the Kotlin tutorial section too, so refer to that section if you're
not sure what enums are. So I'll put this definition above the class here,
the class definition, so that's enum, class, then DownloadStatus,
then we put our left and right curly brace there. Then within there we put
OK comma space, IDLE comma space NOT_INITIALIZED,
comma space FAILED_OR_EMPTY comma space PERMISSIONS_ERROR comma space,
and the last one, ERROR. So the download status enums got
all the states that this class is going to be in, in other words all the possibilities.
So OK means that we've got some valid data.
IDLE means that it's not processing anything, NOT-INITIALIZED means that we haven't got a valid URL
to download.
It's an error condition, because we won't be able to set it until a download's been attempted. Now
FAILED_OR_EMPTY means that we either failed to download anything or the data came back empty.
And the last two errors there, PERMISSIONS_ERROR and ERROR, indicate errors either with the permissions
or with some other error that we haven't identified.
Now you can obviously go into more detail and create more statuses if you really wanted to, but that
will work for our purposes here.
So the next thing I want to do is add a log tag for our debuggings. So let's do that, this time within the
class, private val TAG equals double quotes GetRawData.
Then on the next line we're going to do a private var downloadStatus equals DownloadStatus.IDLE, and you
can see that I've also declared a variable to hold the download status, and we've initialized that to IDLE
because our class instance won't be doing anything when it's first created. Now we've got an error because
we haven't implemented the required methods for an async task.
So let's actually sort that out. Now we need to override the required async task functions,
so we can do a command n, making sure you're within the class definition, or alt insert on a PC, and then
select override methods. Alternatively command
O on a Mac or control O, would get straight to here as well.
So the ones we want here are doInBackground and we also want onPostExecute,
so I'm going to select both of those, then click OK.
The error's now disappeared as you can see. Now the doInBackground
function's going to be pretty similar to the code in the top 10 downloader. There are minor differences, because
we're going to set the download status, and I also want to introduce a catch block to trap any errors, but we'll
come to that in a minute.
So let's start some coding there, we'll delete TODO, and we're going to put if parentheses params, zero
in square brackets,
is equal to null, open a code block and there's going to be download status is equal to download status dot NOT
INITIALIZED, return, then in double quotes No URL specified.
So this first bit of code that I've added just checks that the URL we get isn't null.
Now the function receives an array of strings or a string
array, if you're more used to thinking of it that way.
What's interesting here is the nullable type string question mark, up here.
So this means that the individual elements of params can be null. Params itself however can't be null,
and we can actually get Android Studio to verify that by changing our
null check. So I could change that there to
putting, if params is equal to null, and when I do that, if I hover over that, you can see 'Condition params is equal
to null is always false'. So we can be sure we definitely get a non null array,
but the individual elements it contains might be null.
So that's what the type,
that's why rather, the type is string question mark, and why we start off with a null check.
So pay really careful attention to the code that Android Studio generates, and
be aware that it can change.
Google change things frequently, and if you generate the same function a week later, you might well get
slightly different code.
In fact if you review the top 10 downloader app, the parameter name was different when it generated the
same function
back then. Alright so I'm going to put that line back, I'm going to undo that, and that warning goes away,
and not only that, it's now doing something useful.
Alright so we checked that we've been given a URL and the function was called. We don't know if it's
a valid URL yet, but we can at least check that we've got something.
If not, if the first element of params is null, then we set the standard to NOT_INITIALIZED,
return the text "No url specified".
So let's now enclose the rest of the code and this function inside a try block, so that we can catch any exceptions
that are raised while downloading.
So I'll do that down here, try, then within that I'm going to put downloadStatus equals DownloadStatus.OK, return
URL in uppercase, parentheses, params 0 in square brackets, closing parentheses dot readText. We
need to accept that import, and again that code's very similar to the top 10 downloader function,
once we reduced it to a single line. Now the import for URL doesn't always get added automatically
and you saw that it didn't for me.
If it doesn't, you can just hold down alt enter, and choose Java dot net dot url, and you see again that I did that.
Now there should only be one value passed in the params array.
So we pick up the first one in params zero, this code here, to make sure that we're using elements zero.
As I said, this may not be a valid URL but we're going to rely on the catch block to detect an invalid URL.
Now we set the status to OK using the constant defined in our enum, which is a common approach.
So in other words we assume everything's going to work fine, but then change the status,
even exceptions,
raised. Now the catch block I'm about to add's the same as we originally had in the top 10 downloader.
So let's go ahead and do that and actually add it, so we're going to do a catch, parentheses e colon exception, then a
code block, and then we're going to put val
errorMessage equals when, then in parentheses e.
Then open a code block, then within that we're going to put is Malformed
URLException, end of that we'll have arrow token and then a code block, then we're going to put downloadStatus is equal
to DownloadStatus.NOT_INITIALIZED, then in double quotes on the next line, doInBackground colon Invalid
URL space dollar sign, left and right curly braces, e.message. And what we might do to save a bit of time
here is we might copy and paste some of these.
So I'm going to put is Malformed exception there. I'm going to copy that,
because we've got a couple more we want to put, we want to try.
Next one is going to be IO exception so let's do that one, so is IOException, and
for that one we're going to put FAILED_OR_EMPTY, the status will be failed or empty, and we'll put for the text "IO Exception
reading data" colon and then we'll put the e.message again, there again. The next one we want is security exceptions,
so let's paste that as well.
So is SecurityException, and for that one we're going to put permissions error, dot PERMISSIONS_ERROR, and for
the actual text let's go with "Security exception Needs permission?", and
and we'll leave that e.message in there for the specific error. Then we also need to do down here, after the last is, we need
to add an else, so else code block, sorry arrow token and then block block, and the else is going to be downloadStatus is
equal to DownloadStatus.ERROR.
So that's that generic error that we haven't been able to identify actually what happened, and the
text we're returning is "Unknown error" colon and then space dollar, left and right curly braces, e.message.
Then moving down, down here now, outside of the val error message,
we have a Log.e this time, because it is actually an error, Log.e TAG, error message, then we're going
to return errorMessage.
Alright so that's our function now and our try catch block.
So basically what we're doing is we're checking for a malformed URL, and then any IO exceptions that
actually are thrown, that's these two here.
And there's also a check for security, or at a security exception, if we forget to request permission to access
the Internet. Now if there was an exception, we'll get to the code after the catch blocks
when we return the error message. So that's our doInBackground function completed.
We made a few changes and some minor improvements, but it's really basically the same code as we
used in the top 10 downloader.
So I'll stop the video here, and in the next one we'll create the code for on post execute.
So see you in the next video.