1 00:00:05,580 --> 00:00:09,630 Alright so let's have a look at the onPostExecute method now. 2 00:00:09,630 --> 00:00:15,180 Now we're going to do things slightly differently here to how we did it in the top 10 downloader app. 3 00:00:15,180 --> 00:00:16,190 now in the top 10 4 00:00:16,190 --> 00:00:23,110 downloader, our async task class was very tightly coupled to the other classes in the app. 5 00:00:23,110 --> 00:00:26,110 So I'm going to open that project so you can see what I mean. 6 00:00:26,110 --> 00:00:28,720 Now you may not be aware of this but Android Studio 7 00:00:28,720 --> 00:00:33,820 lets you have several projects open at once, although if you haven't got much RAM it may not be a good idea, but 8 00:00:33,820 --> 00:00:40,600 you can actually do it. So if I come up here to the file menu in Android Studio, I can select Open Recent, and 9 00:00:40,600 --> 00:00:44,290 I can see a list of the projects that I've opened previously. 10 00:00:44,290 --> 00:00:50,110 I'm going to select top 10 downloader, and if I hadn't worked with it recently I could have used the File 11 00:00:50,110 --> 00:00:53,640 Open, and browsed to the project directory instead. 12 00:00:53,640 --> 00:01:00,040 Now we've got this prompt here, and Android Studio then asks if I want to open it in this window which'll close 13 00:01:00,040 --> 00:01:02,380 the current project, or in a new window. 14 00:01:02,380 --> 00:01:08,440 So I'm going to choose new window, so I can have both projects open at once, and by the way that can be useful, 15 00:01:08,440 --> 00:01:12,220 and you may want to do that so you can have my source code open at the same time as yours, 16 00:01:12,220 --> 00:01:18,450 if you're checking for problems. Alright so I'm going to click on New Window. 17 00:01:18,450 --> 00:01:23,010 Alright, so what do I mean when I say tightly coupled. Well firstly let's have a bit of a look here. 18 00:01:23,010 --> 00:01:28,740 I'm just going to close down some of these things. So have a look at MainActivity, and we come down here to the onPost 19 00:01:28,740 --> 00:01:35,900 Create, right down the bottom here, the onPostExecute method for our download data class. 20 00:01:35,900 --> 00:01:39,420 That's actually in here, it's our companion object, so onPost 21 00:01:39,420 --> 00:01:46,860 Executed as you can see here on line 134. Now what it's doing is that it's creating a parse applications 22 00:01:46,860 --> 00:01:52,260 object, and telling it to pass the data that was downloaded. Now that's fine, 23 00:01:52,260 --> 00:01:58,080 there's nothing wrong with that approach, but it does mean that downloading the data and parsing it, treat 24 00:01:58,080 --> 00:02:00,000 it as separate operations. 25 00:02:00,000 --> 00:02:04,830 In other words the same class that downloads the data also starts the parsing. 26 00:02:04,830 --> 00:02:10,500 Now even though it doesn't perform the parsing itself, it's still responsible for deciding what to do 27 00:02:10,500 --> 00:02:11,640 with the parsing. 28 00:02:11,640 --> 00:02:17,310 So it really can only be used for downloading RSS feeds in XML format, because attempting to 29 00:02:17,310 --> 00:02:22,290 use a parse applications object with any kind of data, will cause problems. 30 00:02:22,290 --> 00:02:27,480 So that's what I mean when I say tightly coupled. Although the main purpose of the download data class is 31 00:02:27,480 --> 00:02:29,010 to download data, 32 00:02:29,010 --> 00:02:35,550 it can really only be used to download XML data that conforms to the RSS specification. 33 00:02:35,550 --> 00:02:37,860 Now to give you an idea of why that's not ideal, 34 00:02:37,860 --> 00:02:43,500 the version of that download data class, in the previous version of the Android Java course that this 35 00:02:43,500 --> 00:02:50,940 course is based on, was used by nearly 40000 students. So as well as all the testing I did when developing it, 36 00:02:50,940 --> 00:02:53,800 it's also been extensively used by other people, 37 00:02:53,800 --> 00:02:59,160 so it's proven to be a reliable class. Now it'd be great to just lift it out of that app, and use the 38 00:02:59,160 --> 00:03:05,880 same class in this one because it's been proven to work, but because of that tight coupling, I can't use 39 00:03:05,880 --> 00:03:09,540 it to download our json data without making some changes, 40 00:03:09,540 --> 00:03:13,760 and as soon as I change it, it then has to be tested again in its new form. 41 00:03:13,760 --> 00:03:18,900 So in other words we lose the benefit of all those hours of testing that it had. 42 00:03:18,900 --> 00:03:20,730 Now in this case it's quite a simple class, 43 00:03:20,730 --> 00:03:24,390 so that's not a great hardship, but as you work on more complex code, 44 00:03:24,390 --> 00:03:27,300 it's worth keeping your classes uncoupled. 45 00:03:27,300 --> 00:03:32,430 That way they can be re-used when you need the same functionality in another app. 46 00:03:32,430 --> 00:03:37,620 So for our new app, our flickr app, we're going to write the rest of the download class a bit differently, 47 00:03:37,620 --> 00:03:41,940 to allow it to be used whenever we want to download any sort of text data. 48 00:03:41,940 --> 00:03:47,810 So I'm going to close this top 10 project, top 10 downloader project. 49 00:03:47,810 --> 00:03:54,930 So we're back now into the FlickrBrowser app, and specifically in our getRawData Class, and let's see what we 50 00:03:54,930 --> 00:03:57,030 can do differently in the onPost 51 00:03:57,030 --> 00:04:04,800 Execute function in this class. So this is the one that was responsible for the closed coupling in the top 10 downloader app, 52 00:04:04,800 --> 00:04:10,700 because it created an instance of the parser and also initialized the adapter for the list view. 53 00:04:10,700 --> 00:04:16,050 Now both of these had nothing to do with downloading data, which is what this class should focus on. 54 00:04:16,050 --> 00:04:22,800 So we're going to keep this class focused on just downloading the data. Before that though, I'm going to log the parameter 55 00:04:22,800 --> 00:04:27,480 we get, and then I'm going to run the program and I'm going to make sure that the doInBsckground method is working 56 00:04:27,480 --> 00:04:34,030 OK. I'm also going to remove the call to super dot onPostExecute, 57 00:04:34,030 --> 00:04:35,710 because it really doesn't do anything. 58 00:04:35,710 --> 00:04:38,420 And I can actually check this by holding down the command key, 59 00:04:38,420 --> 00:04:44,200 or control on a PC, and then clicking on this super method, so up here, in the onPostExecute, 60 00:04:44,200 --> 00:04:52,060 I can click on that to view the source code. So as you can see there, for the onPostExecute source code, 61 00:04:52,060 --> 00:04:56,240 there's no code to execute. The onPostExecute method's blank. 62 00:04:56,240 --> 00:05:01,780 Now there's also an annotation up here to suppress the unused declaration warning, 63 00:05:01,780 --> 00:05:04,810 let's see this on line 427, 64 00:05:04,810 --> 00:05:10,510 which is another good indication that this method in the async task base class doesn't do anything. 65 00:05:10,510 --> 00:05:16,800 So in other words it's there purely to provide the method for us to override. The async task will call our method 66 00:05:16,800 --> 00:05:22,120 if there is one, but there's no point us calling that super method, because in this case it's empty. 67 00:05:22,120 --> 00:05:25,000 Now it doesn't normally do any harm if you leave it in, 68 00:05:25,000 --> 00:05:29,060 but you'll see later why I've been pretty keen to remove it this time. 69 00:05:29,060 --> 00:05:33,760 So let's go back to the getRawData class in the onPostExecute. 70 00:05:33,760 --> 00:05:41,160 So I'm going to delete that line, and instead replace that with a logging line, Log.d parentheses 71 00:05:41,160 --> 00:05:51,730 TAG comma, double quotes onPostExecute called, parameter is and dollar result. 72 00:05:51,730 --> 00:05:58,240 Next we need to execute the async task from MainActivity, and specifically from the onCreate function. 73 00:05:58,240 --> 00:06:10,140 Let's go back to main activity, a bit more space here, so in onCreate, down here, after the setSupportActionBar line, we're 74 00:06:10,140 --> 00:06:27,380 going to type val getRawData is equal to GetRawData, parentheses, then getRawData.execute, and in parentheses, 75 00:06:27,380 --> 00:06:32,600 and in there what I'm going to do is paste in the link for the json feed that we had from our browser 76 00:06:32,600 --> 00:06:38,400 last time, and paste it in there because that's the argument that's going to be sent to the getRawData 77 00:06:38,400 --> 00:06:47,560 class, or specifically the execute function. OK, so let's run this app. 78 00:06:47,560 --> 00:06:51,650 Now we won't see anything on the emulator screen because it's still going to show Hello World, but we will 79 00:06:51,650 --> 00:06:58,010 be able to see things working in the log cat, starting with the security exception when I run the app. 80 00:06:58,010 --> 00:07:05,510 Let's actually have a look, look in our log cat, and we've still got our filter on. 81 00:07:05,510 --> 00:07:13,010 I'm going to take the filter off, because we also want to see the code in getRawData showing, and we can see 82 00:07:13,010 --> 00:07:18,120 now some of the logging, but we can also see this error, 'doInBackground Security exception Needs 83 00:07:18,120 --> 00:07:22,640 permission? Permission denied, missing Internet permission'. 84 00:07:22,640 --> 00:07:26,990 And after that onPostExecute called you can see that parameter is doing background, and we've actually got 85 00:07:26,990 --> 00:07:33,470 the message being returned there. So in other words our catch block's working to trap the permissions error. 86 00:07:33,470 --> 00:07:37,030 So to test the download I need to add the Internet permission in the manifest, 87 00:07:37,030 --> 00:07:41,760 so let's go ahead and do that. I'll open our manifest, double click it. 88 00:07:41,760 --> 00:07:47,430 Alright then from there, we're going to add our permissions as we've done previously, then above the applications line 89 00:07:47,430 --> 00:07:57,050 it's going to be, uses permission, and the name is going to be, we're going to just type INTERNET, and press enter there 90 00:07:57,050 --> 00:07:59,530 and we're going to close off that tag. 91 00:07:59,530 --> 00:08:04,100 Remember to select that from the drop down rather than typing it, to avoid any chance of problems 92 00:08:04,100 --> 00:08:08,420 coming up there, with errors, with mistyped name. 93 00:08:08,420 --> 00:08:12,710 Alright so I'm going to run this again now. We should make a bit more progress. 94 00:08:12,710 --> 00:08:21,540 Let's have a look, and the emulator won't do anything different but we'll be able to see some more output, 95 00:08:21,540 --> 00:08:28,250 and you can see now, some data there obviously being returned. 96 00:08:28,250 --> 00:08:30,630 And by the way if we want to clear up things to make it a bit easier, 97 00:08:30,630 --> 00:08:34,980 you've seen before how we can filter. We can use the pipe character to do a double filter so 98 00:08:34,980 --> 00:08:44,400 main activity, pipe, get raw data. That can be easier to only return the logging for the things that we're interested 99 00:08:44,400 --> 00:08:46,420 in looking at. 100 00:08:46,420 --> 00:08:51,770 And note if we scroll down to the bottom that not all data will be logged, because there's a limit on how much you 101 00:08:51,770 --> 00:08:53,180 can log in a single log call, 102 00:08:53,180 --> 00:08:57,560 but if anything had gone wrong with the download we'd have an exception, and we saw an example of an 103 00:08:57,560 --> 00:09:02,480 exception with a security exception, before we fixed that in the manifest. 104 00:09:02,480 --> 00:09:05,510 We don't need to see all the data to know that it is working. 105 00:09:05,510 --> 00:09:10,310 So in other words, at this point our doInBackground function's working. Now it's a good idea to test 106 00:09:10,310 --> 00:09:14,120 functions like this as you go along, that way if there's an error, 107 00:09:14,120 --> 00:09:16,710 you've already narrowed it down to a small bit of code, and 108 00:09:16,710 --> 00:09:19,390 that makes debugging a lot easier. 109 00:09:19,390 --> 00:09:25,670 Alright, so how do we keep the get raw data class uncoupled from the other classes in the app? So we know 110 00:09:25,670 --> 00:09:30,170 that at some point we're going to want to process the data that's been downloaded. 111 00:09:30,170 --> 00:09:36,800 If you want to decouple this class then the onPostExecute function isn't the place to kick off that processing. 112 00:09:36,800 --> 00:09:43,100 So what we really want to do is notify the calling class that the data's being downloaded, and somehow 113 00:09:43,100 --> 00:09:45,470 make the data available to it. 114 00:09:45,470 --> 00:09:51,430 So the basic idea is that the calling class uses get raw data to download the data from some URL. 115 00:09:51,430 --> 00:09:56,750 So get raw data then does the downloading, and when it's finished it calls the class back, using what's 116 00:09:56,750 --> 00:10:02,540 known as a callback method. Now that sounds a lot like what happens when a button gets tapped. 117 00:10:02,540 --> 00:10:08,720 The class that wants to know about the tap, creates an object that's got a function to respond to the click. The 118 00:10:08,720 --> 00:10:11,750 calling class then passes that object to the button. 119 00:10:11,750 --> 00:10:15,650 These are the onClickListeners that we've been creating to respond to button clicks 120 00:10:15,650 --> 00:10:22,470 in previous apps in this course, but here what we want to do is respond when the download is complete. 121 00:10:22,470 --> 00:10:28,610 So to see how this can work I'm going to create an on download complete function in main activity, and 122 00:10:28,610 --> 00:10:32,980 it's going to be called when the download finishes, and we'll do that part in a moment. 123 00:10:32,980 --> 00:10:39,590 But to start I'll just go back to main activity, and we're going to close down the manifest too for now. And we'll close down our 124 00:10:39,590 --> 00:10:45,230 log cat, and what we want to do is create this new function onDownloadComplete. 125 00:10:45,230 --> 00:10:49,430 So we'll do that in the bottom of the file, 126 00:10:49,430 --> 00:11:00,150 so fun onDownloadComplete parentheses data colon string status colon, 127 00:11:00,150 --> 00:11:08,340 and we'll use a DownloadStatus for that which is our enum, open a code block and the code is going to be, if parentheses 128 00:11:08,340 --> 00:11:16,420 status is equal to DownloadStatus.OK, code block. We're going to log it, so 129 00:11:16,420 --> 00:11:32,870 Log.d parentheses TAG, double quotes onDownloadComplete called, data is dollar data, 130 00:11:32,870 --> 00:11:50,600 else, download failed, Log.d parentheses TAG comma onDownloadComplete failed with status dollar status, 131 00:11:50,600 --> 00:12:01,360 error message is dollar data. So this function's got two parameters, 132 00:12:01,360 --> 00:12:06,910 the data, which may be an error message if things go wrong with the download, and the status. 133 00:12:06,910 --> 00:12:12,310 Now we're going to throw all this code away, but sometimes you have to do that while developing. 134 00:12:12,310 --> 00:12:18,040 I've created this function just to get things working, and see how it works, and once we've got the basic 135 00:12:18,040 --> 00:12:21,700 implementation working we'll change it quite a lot. 136 00:12:21,700 --> 00:12:26,940 Now developing small bits at a time like this, lets you test your code as you build the app up. 137 00:12:26,940 --> 00:12:35,260 Alright, so we now need to tell the get raw data class about this onDownloadComplete function so it knows to call it. 138 00:12:35,260 --> 00:12:39,820 Now we could add a method called something like, set download complete listener, or something along those 139 00:12:39,820 --> 00:12:46,180 lines, which is how the Android widgets do it. Now for a widget, it makes sense to have a separate function 140 00:12:46,180 --> 00:12:47,470 to set the listener. 141 00:12:47,470 --> 00:12:53,680 Because they can either be created in code, or by inflating XML, their constructor wouldn't be an appropriate 142 00:12:53,680 --> 00:13:00,070 place to set the listener, but get raw data is only created to download data, and it's got no user 143 00:13:00,070 --> 00:13:03,430 interface or anything like that to complicate matters. 144 00:13:03,430 --> 00:13:09,820 So in that scenario it makes sense to set the listener at the same time as we create the object, by passing 145 00:13:09,820 --> 00:13:12,370 the listener in the constructor. 146 00:13:12,370 --> 00:13:17,170 However many students struggle with this concept of call back functions. 147 00:13:17,170 --> 00:13:22,270 So what I'm going to do is start off, in the next video, in the same way as a button, by adding a 148 00:13:22,270 --> 00:13:26,020 setDownloadCompleteListener function, and get raw data. 149 00:13:26,020 --> 00:13:27,590 So let's do that in the next video.