In this video, we're going to write the class that'll be responsible for parsing the json data that we've
downloaded. So I'm going to go ahead and create a new class,
right clicking the package,
New, Kotlin File/Class, I'm going to select Class to save a bit of typing, and this one is going to be called
GetFlickerJsonData.
Alright so this class will be an async task, so we're going to start off very similar to get raw data,
so add parentheses after the class name, and we're going to do private val listener colon OnDataAvailable,
then right parentheses colon, then async task,
and then we want a diamond operator, less than and greater than sign, signs string comma Void
comma. This time I'm going to do something different, instead of putting string for the third argument,
it's going to be array list, diamond operator again, that's the less than
and greater than sign, operators, in it photo.
Then we need to add parentheses after that as well, before the left
curly brace. Now this is an async task that takes a string and returns a list of photo objects.
Now once again we're not using the progress parameter,
so that's why we're leaving this second argument as void.
Now there's a couple of errors here.
Firstly we haven't implemented the doInBackground method, so we need to do that to avoid that error,
but also we haven't defined the callback interface yet.
I'm going to add a tag for logging as well, so let's do that before we add the interface
as well, private val tag is equal to double quotes GetFlickrJsonData.
Now let's create the interface, so interface,
OnDataAvailable, left and right curly braces. Then we put fun onDataAvailable,
parentheses data colon list, then the diamond operators, less than and greater than, photo, then closing right parentheses,
and on the next line, fun onError, parentheses exception colon Exception with a capital E.
Alright so what have I done here. Well we want to let whatever uses this class, know if there's been an error.
Now we could use an enum, just like we did in get raw data, and include that in the onDataAvailable function.
That would work fine, and
we've already seen how to do something like that already, but I've taken a different approach here, and
I've done that because it's not as obvious what could go wrong when passing json. Now http errors are
clearly documented, and
we could've returned the http response code instead of a status in get raw data,
but here we don't have such a ready list of failure codes.
Now the json parser throws exceptions when things go wrong.
Our get Flickr json data class will pass the exception onto the caller, via this onError function that
we've defined in the interface.
This also provides an opportunity to show one way of dealing with exceptions when they happen on another
thread. Now a normal try catch block isn't much use, because the calling code would have moved on by the
time our async task executes.
So what I mean by that is, you can't do something like this in main activity, when we call the get
raw data dot execute method.
So I'm just going to type some code and show you what I mean. So basically
what I'm saying is, after this line here, we cannot do try then catch, E colon exception, and then put our execute in there.
I'm just going to comment, delete that commented out code as well now. So as we've already seen, our onCreate
function ends before the coding get raw data is executed.
So if get raw data throws an exception, we'll no longer be inside this try catch block,
so the exception won't be caught. And you can't catch an exception that's thrown by code, executing in a
different thread, which is what I've just attempted to do here.
So I'm going to put that code back to how it was, and obviously to make that code valid we would've put something
like that, and put some code in here to do some error processing.
So the point wasn't the fact that we had an error there.
The point was again, you can't catch an exception that's thrown by code executing in a different thread,
and again that's what we're trying to do here if we left the code like this.
So I'm just going to undo those code changes, so we have it back to how it was prior to that.
What I will do is delete some of this commented code from previous videos.
Alright so let's go back to get Flickr json data again.
OK, so what I've done is add another function to the interface, the onError function.
And if our code had an error, or has an error, while parsing the json data, it's going to call the onError function
and pass the exception as an argument. The code that uses get Flickr json data can use that exception
to report the error to the user, or do whatever else it needs to do with that error. And we'll simulate an
error when we've got the code written so we can say this in action.
So moving on we need to implement the doInBackground function that an async task must have. Now doIn
Background is going to perform the parsing of the data.
We're going to give it a string containing the json, and it'll parse the data out into photo objects.
So I'm going to get Android Studio to create the stub for me using control o, we'll do that, that works on both operating systems.
Now the shortcut to implement functions or methods is usually control i,
but that only shows required interfaces.
So when I use control i, we do get the get doInBackground function but not onPostExecute.
So I'm going to cancel that and use control o instead, and
you can see now that we've got both our doInBackground and onPostExecute. So I'm choosing both of
those and clicking on OK, and you can see now the class definition error has now gone
away because we've managed to fix that, by implementing those required functions,
or at least we've created the stubs for them anyway.
So let's go ahead and put a bit of code in there, at least a logging bit of code for the doInBackground. So we'll
do a Log.d parentheses TAG comma doInBackground starts.
What I might do is I'll just undo that. I'll leave the TODO in there for now,
but I will put the logging back in again, because we obviously haven't implemented any code in
the doInBackground.
And for the onPostExecute, we'll leave the super call in there. We'll also paste in the TAG again, but this time we'll
obviously change the name of that to onPost
Execute starts. So onPostExecute starts, we'll leave the call to super there as well.
So I've logged the entry into the functions as usual, and I've mentioned a few times
now, that's a great way to understand what's going on while you're learning. Alright so let's write the doInBackground
function first.
Now we're going to use to the java org dot json package to parse our data,
and that's going to throw a json exception if there's an error in the data.
So I'm going to wrap all the parsing in a try block, and I'm going to delete the two TODO line now because
we we're about to do that. But a good idea though with this TODO, to leave that in your code for stubs that
you haven't implemented yet, just as a reminder that you need to go back and change it. As soon as you start doing or implementing
the code, delete the TODO as I've done there. Alright so we're going to start,
try, we'll add a code block, and within the code block we'll put val json data is equal to json object, note
the capitalisations there, parentheses params, square brackets zero in the middle, then we're closing our parentheses.
Now we're getting an error here because we haven't yet caught any exception that may be thrown, but
that'll go when we add the catch block.
Now to parse json data, using the classes in the org.json package, we create a new json object
and parse the data to its constructor, and we're doing that on line 23.
Now before I move on, notice the parameter that's declared for doInBackground. Android Studio updated
since I recorded the top 10 downloader app. We've now got a slightly better name for the parameter, params here.
It's a bit better than p zero which it was previously.
Now the information we want is contained in a json array called items.
So I'm going to add the code to read the array from the json data, then we'll have a quick look at our source
data again. So on the next line we're going to type val items array is equal to json data dot get
json array, parentheses, then in double quotes items.
Now when we were looking at the flickr json data earlier, we saw that all the values have names. So I'm going to
switch back to Firefox, and you can see up here, is items.
So that's the array containing all the items we're interested in, starting with this,
we can't really see the square bracket, because we're now not looking at the raw json data,
we're looking at formatted data. What we can do though is just open another browser, I'm going to do that now.
We'll open Chrome, then I'll paste that same link in, and we'll see the raw representation.
So you can see over here we've got items, we've got that left square bracket that I was talking about.
So that's basically an array containing all the items we're interested in, and
again it starts with that first opening bracket, and you can see the groups of items separated by a comma,
and the opening and closing, the left and right curly braces.
So within that array, in each of the items consists of these values between those braces, title, link, media etc.
So the first thing we need to do is get the array object, which is all the individual items, and then
we can go through the array and extract out the individual items, and basically access the values we
want. Go back to the code. So we've got this code here on line 24,
then it gets a json array object from the data by calling the get json array function, and specifying
the name of the array, which is items. Now obviously that's a match for this name of the item, name of the array, in the json data, and
we can see that also in Firefox, it's called items as well.
What we can do is loop through all the items in a json array, just as we can with a normal array.
Although there are some differences in the behavior, because we can't assume that all the objects in
a json array are of the same type. In other words, there could be other arrays inside this array.
Now if you want more information or this org.json package, Google have actually produced some decent
documentation, specifically on the Android implementation. I'm just going to go quickly to a browser and we'll
paste that in. So check that out.
You'll find out a lot more about these classes and what they actually do.
Alright so back to Android Studio.
So at this point what we need to do is loop through the array, pulling out the information we're interested in.
So let's go ahead and do that.
So on the next line I'm going to do for, parentheses i in zero until itemsArray dot length, closing parentheses,
and open a code block, and within the code block I'm going to type val json photo is equal to itemsArray dot
getJSONObject,
noting it's getJSONObject and not get json array this time. Then parentheses i, so we're getting that individual
photo object from within the array.
Then what we want to do is go through and get each individual property within that particular entry. So we're going to start with
val title is equal to json photo dot getString parentheses double quotes title.
Let's add a few more, we need another five, we're going to do author, author ID, tags,
json media and also link, so we'll do another five there.
So the next one's going to be title,
sorry the first one was title, the next one's going to be author, and we're going to do author id, tags, json media.
That's going to be a different line of code which we'll talk about shortly, then photoUrl.
Then let's go ahead and change it on the right hand side, so we've got title as the first line, author, the field name
is author, next one, author id is author underscore id,
and you go back and check the names to make sure that we're typing them correctly.
You want to make sure that they're identical. We can see in this particular case,
author underscore id is the actual name,
so making sure that they both match in the code. Alright so moving on, tags is the next one.
json media I'm going to leave because we're going to do something separate with that,
and the last one, val photoUrl, we're going to do get string, m.
And there's actually one more we need to do and I'm going to paste that in as well,
and that one's going to be link, but we're going to do something slightly different with that.
So we've got json media, so up until here, everything's pretty straightforward, the title, the author, the author
ID and the tags,
but json media, here we're going to do something a little bit different. So instead of putting dot getString,
we're going to put dot getJSONObject parenthesis media.
And from within that, on the next line the photoUrl is extracting out the property m for that particular
object media. Then the link,
we're also going to do something similar to that,
so photoUrl dot replaceFirst m.jpg comma underscore b.jpg.
Alright so I'm going to explain this now, what we've just done. So inside this loop, this code here,
what we're doing is we're retrieving the json objects one by one and pulling out the string values
that we need.
But the one that needed special attention that I alluded was line 33,
the media value. The string that we want is embedded inside it,
but in another object called m. So if we go back and have a look
you can see therefore our media, this media there.
We've got another object m, which is embedded inside that,
and that's the reason that we're first retrieving media, then we're retrieving the string, but from the json
object photo that was returned from the call to
jsonPhoto.getJSONObject, and that's how we're getting the url. Now the link here is also
interesting. To explain what's going on there
I'm going to start by explaining what we're going to use those fields for.
Now the photo url value will become the image field of the photo object, so it's passed as the last parameter
to the constructor.
Now if we have a look at the photo class, the last parameter the constructor sets the value of
image, right up here.
So we're going to be using that field to display the image for each photo in the list.
Now when an item in the list is tapped, we'll launch another activity to display the photograph much larger,
so that it fills the screen.
So to do that we're going to use this link value, link value, so going back to GetFlickerJsonData.
So image will give us the url or the photo to show in the initial list, and link will provide the url
of the full size picture. Now whenever we're going to be using urls in an app,
it's a good idea to check them out in a browser first, to make sure they work and provide what you expected.
So let's do that. So going back to the data again,
and for one of these images, I'm going to choose this one here,
I'm going to paste that in another tab. And you can see that loads the picture fine,
so we can be happy with that one,
and you could test a few more at random and if this was a mission critical app, then you probably should,
but I'm happy with this for now.
But next is the link value, so again I'm going to copy that and paste it into a new tab.
Lets copy that again, new tab, paste it in.
Now this looks good from the point of view of displaying the photograph, but it hasn't actually loaded
the photo, again it,
instead it's gone into sort of a slide show page, where you can move backwards and forwards. You can see the
little slide show indicators there, through a set of photos.
So that's great for viewing in a browser, but not much use to us for displaying the image full screen.
So let's have another look at the flickr page. I'm going to copy everything in the url and up to, but not including, the format parameter.
You can go back and open a new tab, and
actually let's go back to Firefox where we can see things are a little bit clearer.
So let's do that, new tab, well actually we'll just copy that exact
url again, and back to Firefox, to Firefox, and we're going to paste that link in again.
It's a smaller link without all the format that equals json etc.
So when I do that in Firefox you can see we're seeing the images,
but obviously back here, in chrome, we're not seeing the images when we do that. We're getting the XML instead.
Now at the bottom of each entry is a little box with the title media files, containing a link to a jpeg.
You'll probably see it more clearly with Firefox, down here media files.
Now the link's slightly different to the one we looked at a minute ago. Instead of underscore m.jpg
it's got underscore b.jpg, see under there, so basically it's a bigger version of the picture,
and this time it's just a simple jpeg rather than a slideshow, which is exactly what we want here.
And we can click on that to see what happens.
We see we just get the image and there's no slideshow there now.
Now because it's documented, it should be safe for us to use this.
So our code replaces underscore m.jpeg, going back to the code. So it's replacing underscore m.jpeg
with underscore b.jpeg in the photo url, to get the url for the larger image.
And if we just have a look at Flickr photos, we've got a reference link here, that gives us a
bit more information about Flickr photos. They are actually documented,
you can see it talks about m being a small size and b being a large size, and because it is documented I think it should
be pretty safe for us to use.
Now going back to this code again here, this code relating to media files. That page is generated on
Flickr servers, so it doesn't appear in the json data that we get back.
So that's why we have to create it in our code, rather than just extracting it from the json data. OK, so now that
we've got the data we want, in the next video we're going to go ahead and start creating photo objects.
So I'll see you in the next video.