1
00:00:05,680 --> 00:00:11,160
Alright, so as I mentioned at the end of the last video, we found that many students can struggle with this concept

2
00:00:11,160 --> 00:00:17,400
of callback functions. So what we're going to do is start off by doing things in the same way as a button.

3
00:00:17,400 --> 00:00:22,760
So we're gonna set up or add a setDownloadCompleteListener function and get raw data

4
00:00:22,760 --> 00:00:26,590
first. Let's go back to that class,

5
00:00:26,590 --> 00:00:33,440
and at the top, just above the override fun onPostExecute, in other words just after the download status

6
00:00:33,440 --> 00:00:43,240
definition on line 19, we're going to add fun setDownloadCompleteListener,

7
00:00:43,240 --> 00:00:53,940
and the parentheses, then it's going to be callback object, colon MainActivity, and add our left and right curly braces as

8
00:00:53,940 --> 00:01:00,550
normal. Then we're going to type listener equals callback object.

9
00:01:00,550 --> 00:01:03,700
Now we're getting an error at the moment because we haven't declared listener.

10
00:01:03,700 --> 00:01:05,080
So let's go ahead and do that.

11
00:01:05,080 --> 00:01:15,820
We'll do that above that line, so private var listener colon MainActivity, question mark,

12
00:01:15,820 --> 00:01:24,340
equals null. And you can see that we've got a warning there about this field leaking a context object.

13
00:01:24,340 --> 00:01:29,320
Ignore that for now because it's only temporary, to show how the callback we're going to create is just the

14
00:01:29,320 --> 00:01:32,290
same as a button click listener.

15
00:01:32,290 --> 00:01:37,990
OK so we know that our onDownloadComplete function's in MainActivity, which is why our setDownload

16
00:01:37,990 --> 00:01:42,880
CompleteListener function defines our main activity type as its parameter.

17
00:01:42,880 --> 00:01:44,560
And you can see that on line 23.

18
00:01:44,560 --> 00:01:50,160
Now listener's going to be null until we call setDownloadCompleteListener to initialize it,

19
00:01:50,160 --> 00:01:54,400
and that's why we've declared it to be a nullable type on line 21.

20
00:01:54,400 --> 00:02:00,880
Now we can call the listeners onDownloadComplete function in the onPostExecute function.

21
00:02:00,880 --> 00:02:09,389
So let's go and add the code for that, after the log, so listener question mark, dot onDownload

22
00:02:09,389 --> 00:02:17,670
Complete, then parentheses, and it's going to be result comma and downloadStatus, the two arguments we're passing to it.

23
00:02:17,670 --> 00:02:23,310
Now we've got an error at the moment because result is a nullable string, and our onDownloadComplete

24
00:02:23,310 --> 00:02:26,760
function needs a non-null string type.

25
00:02:26,760 --> 00:02:32,100
Now onPostExecute's going to be passed the return value from doInBackground,

26
00:02:32,100 --> 00:02:35,460
and we've made sure that doInBackground doesn't return null.

27
00:02:35,460 --> 00:02:39,800
In fact it's return type is the non-nullable string type.

28
00:02:39,800 --> 00:02:46,290
And you can see that again here on line 26. Now changing the function signature to accept a string,

29
00:02:46,290 --> 00:02:48,170
isn't going to cause any problems here.

30
00:02:48,170 --> 00:02:53,760
So we'll do that to fix the error. I'll change that, and you can see the error then disappears.

31
00:02:53,760 --> 00:02:59,010
Now we do have to use a safe call when calling the onDownloadComplete method,

32
00:02:59,010 --> 00:03:01,200
and that's because listener might be null.

33
00:03:01,200 --> 00:03:06,900
In other words it's no guarantee that the caller will call the setDownloadCompleteListener function

34
00:03:06,900 --> 00:03:13,230
before starting the async task. Now that's definitely true, because we're not calling it yet.

35
00:03:13,230 --> 00:03:16,500
Now we need to do that before starting the async task,

36
00:03:16,500 --> 00:03:20,370
in other words, before we call the execute function in main activity.

37
00:03:20,370 --> 00:03:30,810
So let's go back to main activity, and go up to this code, and actually before the call to dot execute, in

38
00:03:30,810 --> 00:03:35,870
between the definition of our get raw data variable, we want to put it in there. So it's

39
00:03:35,870 --> 00:03:44,670
getRawData.setDownloadCompleteListener and in parentheses, this, is the argument.

40
00:03:44,670 --> 00:03:51,930
Now I'll explain what 'this' is in a moment. So let's actually try running it though to make sure it works,

41
00:03:51,930 --> 00:04:01,970
and we'll also open log cat.

42
00:04:01,970 --> 00:04:06,020
Now we can see here if we go back and have a look there's a lot of data there.

43
00:04:06,020 --> 00:04:10,990
And in fact the json data has been logged twice in the log cat,

44
00:04:10,990 --> 00:04:13,820
and you can see there's two versions of that,

45
00:04:13,820 --> 00:04:22,240
but the one we're looking at here is the logged output from onPostExecute. But a bit further down, and you can see

46
00:04:22,240 --> 00:04:27,050
onDownloadComplete is also printing out the json data aswell.

47
00:04:27,050 --> 00:04:28,460
So that's working fine overall.

48
00:04:28,460 --> 00:04:34,430
When the async task finishes executing, it calls its onPostExecute function.

49
00:04:34,430 --> 00:04:40,520
Now the onPostExecute function calls the onDownloadComplete function in the listener, which is a current

50
00:04:40,520 --> 00:04:42,620
instance of our main activity.

51
00:04:42,620 --> 00:04:49,190
And again we can see that here, onDownloadCompleteownload called, and there's our function onDownloadComplete, so

52
00:04:49,190 --> 00:04:51,880
it's being called in there.

53
00:04:51,880 --> 00:04:58,070
And this is exactly the same as what happens when you tap a button. You register your activity, or an anonymous

54
00:04:58,070 --> 00:05:00,570
inner class that it contains as a listener,

55
00:05:00,570 --> 00:05:04,370
by calling the setOnClickListener function of the button.

56
00:05:04,370 --> 00:05:10,130
Then when the button's tapped, the button calls your activities onClick function.

57
00:05:10,130 --> 00:05:18,470
So here what we're doing, we're registering our activity as a listener, according to set onDownloadComplete function. 

58
00:05:18,470 --> 00:05:24,600
You can see that code being executed on line 20, and when getRawData finishes downloading, it'll call our

59
00:05:24,600 --> 00:05:27,090
onDownloadComplete function.

60
00:05:27,090 --> 00:05:31,890
That's why we're passing this to the call, to setDownloadCompleteListener in MainActivity.

61
00:05:31,890 --> 00:05:35,550
This refers to the current instance of a class.

62
00:05:35,550 --> 00:05:39,770
Now you can think of Kotlin automatically doing something like, 

63
00:05:39,770 --> 00:05:48,170
if I just type it up here, private val this equals MainActivity. So

64
00:05:48,170 --> 00:05:51,370
basically think of Kotlin automatically doing something like that.

65
00:05:51,370 --> 00:05:53,150
Now it doesn't really do that,

66
00:05:53,150 --> 00:05:57,410
and I've got an error when I tried, but that's effectively what this is.

67
00:05:57,410 --> 00:06:03,500
It's an instance of a class, MainActivity in this case, that's running the code in the class.

68
00:06:03,500 --> 00:06:11,480
So by passing this, and I'll just delete this now, so by passing this down here to setDownloadcomplete

69
00:06:11,480 --> 00:06:17,120
Listener, we're telling getRawData that it should call the onDownloadComplete function

70
00:06:17,120 --> 00:06:24,240
in this instance of Mainactivity. Alright, now I'm in danger of labouring this point a bit much, so

71
00:06:24,240 --> 00:06:25,560
I'm going to move on.

72
00:06:25,560 --> 00:06:30,060
We use listeners and callbacks a lot in this course, and I will be explaining it all again in a different

73
00:06:30,060 --> 00:06:36,930
way, in a later section. But it is something that students have struggled with in the past, but it's also

74
00:06:36,930 --> 00:06:39,760
an essential part of event driven programming.

75
00:06:39,760 --> 00:06:44,190
Windows and the various Linux GUI's also make extensive use of callbacks,

76
00:06:44,190 --> 00:06:49,320
so it's not just an Android thing. It's a really good thing to learn and really fundamentally understand

77
00:06:49,320 --> 00:06:51,360
as a developer.

78
00:06:51,360 --> 00:06:57,540
OK so moving on, we're going to change that code. So the base functionality remains the same,

79
00:06:57,540 --> 00:07:00,870
but now what we're going to do is implement it differently.

80
00:07:00,870 --> 00:07:06,630
Now we might want to call our getRawData from different classes, not just from MainActivity classes,

81
00:07:06,630 --> 00:07:12,660
so that means we need to ensure there's some way that the class does have an onDownloadComplete function.

82
00:07:12,660 --> 00:07:18,900
So what we're going to do is change the way we create the getRawData object, by adding this as an argument

83
00:07:18,900 --> 00:07:24,900
when we create it. So at the moment we've got val getRawData equals getRawData parentheses.

84
00:07:24,900 --> 00:07:29,290
We're going to add this as an argument.

85
00:07:29,290 --> 00:07:35,040
We've got an error there because getRawData class is expecting that argument.

86
00:07:35,040 --> 00:07:41,460
But also what we're going to do is now delete this next line, well I'll comment it out at least, as we won't need that

87
00:07:41,460 --> 00:07:50,640
after we make this change. So going back to getRawData,

88
00:07:50,640 --> 00:07:57,960
we need a field to store the object whose method we need to call, and then we'll set that field in the constructor.

89
00:07:57,960 --> 00:08:01,770
So looking at our class definition line on line 17,

90
00:08:01,770 --> 00:08:07,780
we're going to change that, we're going to add parentheses after getRawData, and we're going to type private val

91
00:08:07,780 --> 00:08:19,200
listener colon MainActivity. I'll leave everything else as it is. Now I've declared the callback object to be of

92
00:08:19,200 --> 00:08:24,930
the type MainActivity, so that we can pass any main activity class object to it.

93
00:08:24,930 --> 00:08:27,900
There is a problem with that, and we'll look at that in a minute.

94
00:08:27,900 --> 00:08:31,800
For now we're just getting things working and understanding how it works.

95
00:08:31,800 --> 00:08:38,429
So now that we've done that, we don't need our private listener object here on line 21, and

96
00:08:38,429 --> 00:08:42,690
we also don't need this setDownloadCompleteListener function,

97
00:08:42,690 --> 00:08:49,530
so I'm going to comment that out as well. Alright so our getRawData class is definitely going to have a non-

98
00:08:49,530 --> 00:08:50,940
null listener now.

99
00:08:50,940 --> 00:08:57,150
So therefore we can remove the safe call operator from the onPostExecute method. So over here,

100
00:08:57,150 --> 00:09:00,120
we can actually get rid of that now.

101
00:09:00,120 --> 00:09:06,660
So when the download finishes and the onPostExecute function's called, it'll call our main activities

102
00:09:06,660 --> 00:09:12,140
onDownloadComplete function, and provide it with the data and the status result.

103
00:09:12,140 --> 00:09:18,310
And this is exactly the same as before, but we're just providing the listener object differently. So we're

104
00:09:18,310 --> 00:09:26,920
going to clear the log cat, got it open, clear that and we'll run it again,

105
00:09:26,920 --> 00:09:31,230
probably don't need to see the interface. So nothing's really changed

106
00:09:31,230 --> 00:09:37,310
there, you can see that we've still got the data returned. We can see that onDownloadComplete is being called, and

107
00:09:37,310 --> 00:09:42,740
we've got two copies of the adjacent data showing. So basically things have worked just like last time.

108
00:09:42,740 --> 00:09:49,220
So you can see onCreate being called there, an onCreate ending, and the data being logged in onPostExecute, and after

109
00:09:49,220 --> 00:09:56,150
logging the data onPostExecute, causing onDownloadComplete function of main activity, and a bit lower

110
00:09:56,150 --> 00:10:00,290
down in the log cat, was the data that I'm showing you on the screen now.

111
00:10:00,290 --> 00:10:03,650
That's again the data being logged again by onDownloadComplete.

112
00:10:03,650 --> 00:10:05,390
So that's how callbacks work.

113
00:10:05,390 --> 00:10:10,790
You create a function that the called object will call when something interesting happens.

114
00:10:10,790 --> 00:10:16,700
In this case we gave the getRawData instance a reference to the main activity object, by using this from

115
00:10:16,700 --> 00:10:23,130
inside the main activity class. Now the problem I've just mentioned is that there's nothing to guarantee

116
00:10:23,130 --> 00:10:29,630
that main activity does have an onDownloadComplete method. Now ours does, but none of the other main activity classes

117
00:10:29,630 --> 00:10:32,090
that we've created did.

118
00:10:32,090 --> 00:10:37,790
So the way to deal with that is by defining an interface that the calling object must implement,

119
00:10:37,790 --> 00:10:40,010
so an interface is a binding contract.

120
00:10:40,010 --> 00:10:46,840
So anything that implements our interface guarantees that it'll implement the functions we specify.

121
00:10:46,840 --> 00:10:49,470
Now this might be easier to see rather than talk about, so

122
00:10:49,470 --> 00:10:53,850
I'm going to start by defining the interface in a getRawData class.

123
00:10:53,850 --> 00:10:55,310
We're going to go back to the top here,

124
00:10:55,310 --> 00:10:58,520
close down our log cat window, and

125
00:10:58,520 --> 00:11:07,850
below our download status definition I'm going to type interface, onDownloadComplete, and open up a code

126
00:11:07,850 --> 00:11:20,360
block, and within there we're going to put fun onDownloadComplete parentheses, data colon, String comma status colon

127
00:11:20,360 --> 00:11:29,880
DownloadStatus. So to define an interface we just declare it in a very similar way to declaring a class,

128
00:11:29,880 --> 00:11:35,410
and specify the functions that must be implemented by anything that implements the interface.

129
00:11:35,410 --> 00:11:39,710
Now you don't actually write any code for the functions. That's up to the implementer.

130
00:11:39,710 --> 00:11:44,330
All we're doing is providing the name, parameters and return type if any.

131
00:11:44,330 --> 00:11:50,120
So now that we've done that we can change the type of our listener object to be onDownloadComplete. Now just like with

132
00:11:50,120 --> 00:11:55,040
classes, the convention is that interface names should start with a capital letter,

133
00:11:55,040 --> 00:11:59,540
so the interface that we're going to use is on download complete with a capital O.

134
00:11:59,540 --> 00:12:04,360
It specifies a single method which I've called onDownloadCcomplete with a lowercase o, and you can see that

135
00:12:04,360 --> 00:12:06,590
on lines 21 through 23.

136
00:12:06,590 --> 00:12:13,790
So now that we've done that, we can change our listener type. So at the moment time we've got var listener main activity.

137
00:12:13,790 --> 00:12:18,010
We can change that to OnDownloadComplete.

138
00:12:18,010 --> 00:12:21,720
So now our listener property's of type OnDownloadComplete.

139
00:12:21,720 --> 00:12:28,040
Now if I tried to run the app now we'd get an error, because main activity isn't an on download complete object.

140
00:12:28,040 --> 00:12:30,100
So we have to fix that first.

141
00:12:30,100 --> 00:12:36,320
So when I switch over to main activity, there's an error that has come up now when we create the getRawData

142
00:12:36,320 --> 00:12:43,280
object, because we can't pass an object of type main activity to a constructor that expects on download complete.

143
00:12:43,280 --> 00:12:49,850
So what we need to do to fix that, is to make main activity implement the on download complete interface.

144
00:12:49,850 --> 00:12:56,820
Now in Kotlin, we specify that by adding the interface name after the superclass in the declaration.

145
00:12:56,820 --> 00:13:04,280
So I'll come up here and start doing that. At the moment we've got class mainActivity colon appCompatActivity. I'm going to type comma, and

146
00:13:04,280 --> 00:13:12,670
if I start typing OnDownload, you can see that Android Studio offers the get raw data dot OnDownloadComplete

147
00:13:12,670 --> 00:13:14,580
interface as a suggestion. And you can't really see all of that,

148
00:13:14,580 --> 00:13:20,860
but we can see that the academy dot learn programming package there and I can press enter there, and you can see that's filled

149
00:13:20,860 --> 00:13:22,760
in for us automatically.

150
00:13:22,760 --> 00:13:28,040
So now main activity's declared as implementing the interface and can be used whenever an object of type

151
00:13:28,040 --> 00:13:30,530
on download complete is needed.

152
00:13:30,530 --> 00:13:35,630
So we can't just pass just any old main activity to the constructor of get raw data.

153
00:13:35,630 --> 00:13:38,630
We have to use one that implements the interface.

154
00:13:38,630 --> 00:13:44,030
And that means that our get raw data class can be sure that the object that it's been given does have

155
00:13:44,030 --> 00:13:49,470
the correct function for it to callback. Now if we scroll down and have a bit of a look down here,

156
00:13:49,470 --> 00:13:51,600
we can see that we've got an error here,

157
00:13:51,600 --> 00:13:57,450
and that's because we haven't marked on download complete as overriding the interface function, and that's something

158
00:13:57,450 --> 00:14:00,630
that Kotlin insists on. I'm going to type in

159
00:14:00,630 --> 00:14:07,660
override there, hope that keeps it happy, and I'm going to say a bit more about that in a minute, but

160
00:14:07,660 --> 00:14:18,130
I'm going to run the app again first just to make sure it's still working.

161
00:14:18,130 --> 00:14:23,320
Now it looks to me that everything's working fine. We've still got our two versions of the json data being

162
00:14:23,320 --> 00:14:27,310
outputted, and it seems that all the other functions are being called back correctly.

163
00:14:27,310 --> 00:14:29,170
So that looks to be working fine.

164
00:14:29,170 --> 00:14:35,770
So let's have another look at this on download complete function in main activity.

165
00:14:35,770 --> 00:14:40,770
Now we've already typed that function in rather than using the code generator, and it didn't have override

166
00:14:40,770 --> 00:14:44,880
specified. Now Kotlin insists that you mark overridden functions with

167
00:14:44,880 --> 00:14:49,210
override, and you saw that when I typed override there, the error disappeared.

168
00:14:49,210 --> 00:14:55,270
So override tells the compiler that the function that follows it is overriding an existing function

169
00:14:55,270 --> 00:15:01,810
in its superclass or an interface. Using override allows the compiler to check that the function has the

170
00:15:01,810 --> 00:15:07,480
correct name and the correct number and type of parameters. Android Studio also uses it to show these

171
00:15:07,480 --> 00:15:12,400
errors and warnings, over here on the right hand side, the right hand margin.

172
00:15:12,400 --> 00:15:20,790
Let's say for example that I typed the function name wrong. If I change it to have a capital L instead for download,

173
00:15:20,790 --> 00:15:23,450
and that by the way, is quite an easy mistake to make,

174
00:15:23,450 --> 00:15:32,780
but you can see when I do that we're suddenly getting errors. We've got an error here, and also an error up here as well.

175
00:15:32,780 --> 00:15:37,520
So the first one is this error here, 'onDownLoadComplete overrides nothing'.

176
00:15:37,520 --> 00:15:43,550
That means that no function with that name in the appcompat activity base class, or the on download complete

177
00:15:43,550 --> 00:15:47,150
interface, exists. There's no function of that name.

178
00:15:47,150 --> 00:15:49,730
And there's also an error in our base declaration

179
00:15:49,730 --> 00:15:54,920
up here. We're getting that error because the main activity no longer implements the correct function for

180
00:15:54,920 --> 00:15:56,390
the interface.

181
00:15:56,390 --> 00:16:01,400
So the override keyword allows Kotlin to check so that we don't make mistakes like this.

182
00:16:01,400 --> 00:16:06,980
So I'm going to go back and correct the spelling to onDownloadComplete to fix that error, and we'll finish the

183
00:16:06,980 --> 00:16:13,220
video here. In the next video we'll add the code for parsing the json data, so I'll see you in the next video.

