In the last video, we looked at how to
use the Android AsyncTask class to run
code in a background thread, so that we
didn't cause our app to block while
waiting for a long-running process, such
as downloading data from the Internet.
Now one thing that I didn't
mention about that doInBackground
method, was the var arg modifier in the
parameter list. Now, variable length
argument lists allow you to provide
several values for the parameter. Now all
the arguments you provide must be of the
same type, so they must all be strings
in this particular case, but we could
pass in several URLs and have them
downloaded one after the other, if we
wanted to do so.
Now we're not actually going to do
that, but the option's there if you need
it. So we're only going to use a single
string value when we get the doin
Background method to do our downloading,
but you can pass multiple parameters
to it if you want to. Now because it's
specified as var arg - and this is here,
you can see on line 30, is what I'm
talking about. The p0 parameter is
accessed as a string array inside the
function. So the first item in the array
is p0,with 0 in square brackets, and
that's the value we included in the
logcat, which is effectively the first
parameter. Now if several URLs were
provided when calling the execute method,
we can access them as p0 square
brackets 1, p0 square brackets 2 and so
on. So what I mean by that is, we can we
can access them to something like p0[1], p0[2],
so on and so forth. Now p0 is an awful
name for a parameter, so I'm going to
rename that to URL which makes a lot
more sense. Now Android Studio will
rename all occurrences for us, when we
right-click and choose Refactor Rename.
So I'm gonna go ahead and do that; select
Refactor then Rename, and I'm
going to call that URL and press Enter.
And you can see, conveniently it's also
changed the reference to what was p0 on
line 31. Now hopefully the code generator
will use a better name in future. The
more usual args would have been better
than p0. Now we saw how to start the
background task, which just involves
creating an instance of our Download
Data class and calling
it's execute method, with any arguments
that it needs. When the AsyncTask
class's execute method's called, it takes
care of setting up all the
multi-threading, and then runs doin
Background on a separate thread, passing
it whatever arguments we gave to the
execute method. Now when the task
completes, the AsyncTask gets the
return value from the other thread, and
then calls the onPostExecute method
with the return value. So with that said,
now let's get our background thread to
do something a little bit more useful. So
we're going to make some changes to the
code now, and I'm just going to close the
logcat for now to make a bit more, I'll
give it a bit more screen real estate.
So we're going to leave the first log
there - the log message. We're going to
come up here and type in val space RSS
Feed is equal to download XML
parenthesis url square brackets with a
zero in the middle. And then on the next
line, I'm going to type if parentheses
rssFeed dot is empty, then open a code
block. We're gonna type Log.e tag, and in
parentheses, doInBackground :
space error downloading, to let us know
there was a problem, and obviously, that's
closing off the code block. Then
on the last line, instead of returning
doInBackground completed as a string,
we're going to return the rssFeed. So when
you use a vararg parameter, the actual
values get passed into the method as an
array - an array of strings, in this
particular case. And because we're only
going to call this method with a single
parameter, we won't actually bother
writing code to get more than one string.
The one we're interested in will be the
first one in the array, and that's this
url[0] which you can see me referencing,
which we've talked about previously. Now
of course, we haven't written the
download XML method yet, but that'll take
care of downloading the feed, and it'll
return a string containing the XML when
it's finished. Now if anything goes wrong
it's going to return an empty string, so
that we can check to see if the return
value is empty, and log an error if
is. Now normally we'd use Log.d for
writing a debug information to the log,
but this will actually be an error. Now
log.d entries shouldn't appear in the
logcat when we release the production
version of our apps. The build process
removes them from the code. But in this
particular case, we'd really want
this message to remain, so I'm using
log.e instead of Log.d to log
this message as an error, rather than a
debug level. And of course, I'm referring to
the entry on line 34. Alright, so all
the code we put in doInBackground will
run asynchronously on a background
thread, and that also includes any
methods that it calls. So that means
download XML will also be running on the
background thread.
Now when the download XML function
returns, we'll either have the XML in the
rssFeed string, in which case we'll just
return it, or if we've got an empty string, in
which case, we're going to log an error
before returning. Now the download XML
function will take a fairly typical
approach. That's pretty well common when
you're reading from a stream of data,
such as data over the internet or data
stored in a file on disk. So we're going
to start by opening a http connection,
which we use to access the stream of
data coming over the internet from a URL.
Now the connection provides an input
stream, so we're going to be using an
input stream reader to read the data
from it. Now whenever you're dealing with
a stream that's coming from a slow
device, such as a disk drive or an
internet connection, it's actually a good
idea to use a buffered reader. Now a
buffered reader, as the name suggests,
buffers the data coming in from the
stream. So instead of repeatedly
accessing the hard drive or network, a
block of data is read into a buffer in
memory, and our program can read from the
buffer. So that's the recommended way to
read data from an input stream, so our
app will use a buffered reader. I'm
going to type the code for that inside
the DownloadData class, and then we'll
actually discuss the actual reading.
Now I've already horrified any Java
developers taking this course, so now I'm
going to turn the tables, and I'm going
to horrify any Kotlin developers in the
course. Now don't worry, this next bit of
code is only
temporary, and we're gonna be rewriting it
into more idiomatic Kotlin shortly. The
entire block of code that I'm about to
type in, in the rest of this video, can be
written in just a few lines as we'll see,
but it is much easier to understand if
we use and see the long version first. So
let's go ahead and do that. So I'm gonna
start with the new function; private fun
downloadXML, and as an argument, it's
gonna be urlPath colon string
question mark colon string, and then
we'll use the left and right curly braces.
And we're going to start by typing val
xmlResult equals string builder. I'm
gonna do a try, then within the try I'm gonna
type val url equals URL urlPath val
connection colon HttpURLConnection.
Make sure you actually type here, the
HttpURLConnection. In the past, students
have actually typed Https, which is an
entirely separate thing. So make sure you
choose the correct one. So that's equal
to url.openConnection
parentheses, then space as HttpURL
Connection, and again, not Https. Next, on
the next line, val response is equal
to connection.responseCode Log.d
TAG and double quotes after the comma,
downloadXML : The response code was, and
dollar response. On the next line, we're
going to type - actually we'll leave a space there
- on the line after that, val inputStream
is equal to connection.inputStream.
Then val inputStreamReader equals
InputStreamReader inputStream, and val
reader is equal to BufferedReader
parentheses inputStreamReader - oops,
parentheses there - then InputStream
Reader. Alright, so starting off on line
forty-six, I'm just using a String
Builder, because we're going to be
appending to the string a lot as we read
characters from the inputStream. Now a
StringBuilder is more efficient than
just concatenating strings, and you'll
see how that works in a minute. So the
next thing you may find strange is the
try block. When dealing with data from an
external source, which basically means
anything that isn't the computer's
memory, there are actually a lot of
things that can go wrong. The device may
not be connected to the Internet, or the
connection may drop while you're
downloading data. It's possible that the
Apple website might be down, which
is rare, but you can't count on any
websites being available 100% of the
time. And it's also possible that the URL
passed into the method isn't valid, which
would prevent the connections from being
made in the first place. So a try block
is a way to wrap up a section of code,
and catch any exceptions that occur
while it's executing. Now the first
exception that would be thrown in that
code, is when we create a URL from the
string that was passed in to download
XML. There's no guarantee that our
download XML method will be given a
valid URL, in the URL path parameter, and
if the method isn't valid, a malformed
URL exception will be thrown. Now
the way we deal with the exception is to
catch it using a catch block. So what
I'm going to do is, just move up here a
little bit more, and after the right
curly brace which closes off the try
block, we're going to need to type catch
parentheses e : and MalformedURL
Exception, and select that from the list.
You'll open a code block, and then type
Log.e parentheses TAG comma space
downloadXML. So invalid URL dollar left
and right curly braces, e.message. Now
I've used Log.e again there,
rather than log.d, because this is an
error and not just debug information. And
you'll see that
entry behind being logged to logcat,
when we come to run the program because,
at the moment, we're calling the Async,
the AsyncTask's execute method with a
very invalid URL - the string URL goes
here. Now the next exception that we may
get - that's an i/o exception.
Now the first place we make an exception
is on this line here, line 51, when we try
to open the connection. In fact, an i/o
exception can be thrown
whenever code performs an input or
output operation, such as reading or
writing to a disk file or, like here,
accessing the Internet.
Now although we have a valid URL - URL if
the code hasn't got that far - it may not
be possible to open the connection if
there's a problem with our internet
connection. Now it's also possible that
the URL may refer to a server that
doesn't exist. Now, for example, if we
spelt to Apple incorrectly as appl. So
we're going to add another catch block
here, under the first one, to catch IO
exceptions. So go ahead and do that.
So after the closing curly brace, catch
parentheses e : IOException, and
then closing parentheses and then open
the code block. Log.E because it's
an error again, TAG in parentheses comma
space, then double quotes. Then it's gonna
be downloadXML :
IO Exception reading data :
then dollar, then left and right curly
braces, e.message again. So as you
can see, you can have multiple catch
blocks and when an exception's
thrown, Kotlin will go through them in
order, until it finds the first one that
matches the exception that was thrown.
Now the order that you catch the
exceptions is important, because in our
code here, a MalformedURLException is
a subclass of IOException. Now we
can check that if we hold down the
command key, and click on the Malformed
URLException, and on a PC running
Windows or Linux that will be the
Ctrl key. So we come down there - go to
to Malformed Exception and click, and
we can jump to the source for the
exception class, as you can see here, and
we can see that it extends
IOException. So MalformedURL
Exception on line ten, extends IO
Exception.
So if we'd caught these exceptions the
other way round, we'd actually never see
the message invalid URL - we'd just get IO
exception reading data, which would make
finding the cause of the problem a lot
harder. Alright, so let's just close
that file down again - that class file down
again. Go back to where we were. Mow
that's actually even more important, if
you add another catch block to deal with
all other exceptions. So I'm gonna come down
here and do that. Add another catch on
the next line, e : Exception, another
code block again. It's gonna be Log.e
parentheses TAG comma space double
quote, Unknown error :
dollar sign, left and curly braces,
e.message. So if we put this last
catch block first, then that would be the
only one executed because all of our
other exceptions are, ultimately, a
subclass of exception. Alright, so let's
end the video here now. In the next video,
we'll start talking more about the Http
connection, and how the server sends back
a response code. So I'll see you in the next
video.