1 00:00:00,000 --> 00:00:02,500 (light music) 2 00:00:05,220 --> 00:00:06,930 We've separated the logic of our app 3 00:00:06,930 --> 00:00:08,520 from the user interface, the UI, 4 00:00:08,520 --> 00:00:11,190 and our activity doesn't do anything 5 00:00:11,190 --> 00:00:15,040 that isn't directly related to interacting with the user. 6 00:00:15,040 --> 00:00:17,780 But I did mention that we shouldn't really be exposing 7 00:00:17,780 --> 00:00:20,880 mutable live data objects because there's a risk 8 00:00:20,880 --> 00:00:23,810 that the view model's owner could change them. 9 00:00:23,810 --> 00:00:26,230 So in this video we're going to look at how to fix that. 10 00:00:26,230 --> 00:00:29,260 If we don't want to expose the mutable live data object 11 00:00:29,260 --> 00:00:31,770 then the first step is to mark them private 12 00:00:31,770 --> 00:00:33,430 in Calculator View Model. 13 00:00:33,430 --> 00:00:36,220 Now class, to do that we're going to do just that. 14 00:00:36,220 --> 00:00:39,720 On line 16, private, 15 00:00:39,720 --> 00:00:41,980 and we'll do the same for line 17 and 18 16 00:00:43,490 --> 00:00:45,790 marking all three as private. 17 00:00:45,790 --> 00:00:49,120 Once we've done that, we go back to main activity. 18 00:00:49,120 --> 00:00:52,360 You can see now that in fact we break main activity 19 00:00:52,360 --> 00:00:55,090 because it can't access the private attributes 20 00:00:55,090 --> 00:00:56,260 of the view model. 21 00:00:56,260 --> 00:00:57,100 What we're going to do though 22 00:00:57,100 --> 00:00:59,940 is create new attributes that it can use. 23 00:00:59,940 --> 00:01:02,700 So I'm gonna modify main activity to use these, 24 00:01:02,700 --> 00:01:05,470 even though they haven't been created just yet. 25 00:01:05,470 --> 00:01:06,450 So we'll be getting an error, 26 00:01:06,450 --> 00:01:09,780 but what we'll do, instead of getting our result 27 00:01:09,780 --> 00:01:13,560 It's gonna be string result, 28 00:01:13,560 --> 00:01:14,910 instead of new number. 29 00:01:14,910 --> 00:01:17,980 It's gonna be string new number. 30 00:01:17,980 --> 00:01:21,520 Noticing I'm changing the capitalization there of the words, 31 00:01:21,520 --> 00:01:23,683 and then string operation, 32 00:01:24,530 --> 00:01:26,030 instead of operation. 33 00:01:26,030 --> 00:01:27,410 And again, we've got errors, 34 00:01:27,410 --> 00:01:29,640 but they'll go once we create the attributes 35 00:01:29,640 --> 00:01:31,890 back in Calculator View Model. 36 00:01:31,890 --> 00:01:34,480 We'll go and start looking at that now. 37 00:01:34,480 --> 00:01:37,220 So for each of our mutable live data objects, 38 00:01:37,220 --> 00:01:39,013 we need corresponding live data object 39 00:01:39,013 --> 00:01:41,700 that the observer can use. 40 00:01:41,700 --> 00:01:44,623 I'm gonna leave result to last, do that other two first. 41 00:01:45,660 --> 00:01:49,000 So I'm gonna start, just making this space with new number, 42 00:01:49,000 --> 00:01:51,360 set below the definition for new number, 43 00:01:51,360 --> 00:01:53,857 we're going to type val stringNewNumber. 44 00:01:56,205 --> 00:01:59,673 It's gonna colon, LiveData, instead of mutablelivedata. 45 00:01:59,673 --> 00:02:02,490 Then, we can define that its a string, so left and right, 46 00:02:02,490 --> 00:02:04,970 less than and greater than signs at the string there, 47 00:02:04,970 --> 00:02:07,140 we're gonna accept that if an employer asks you to do that, 48 00:02:07,140 --> 00:02:08,673 you can accept that, 49 00:02:09,917 --> 00:02:11,797 and then on the next line, 50 00:02:11,797 --> 00:02:15,350 we'll do a tab, and type get, in parentheses, 51 00:02:15,350 --> 00:02:17,113 equals new number. 52 00:02:18,250 --> 00:02:19,400 That's our first definition, 53 00:02:19,400 --> 00:02:21,533 and let's do the same now for operation. 54 00:02:23,140 --> 00:02:24,720 So below the definition of operation, 55 00:02:24,720 --> 00:02:27,600 val string operation, 56 00:02:27,600 --> 00:02:30,203 colon, live data, 57 00:02:30,203 --> 00:02:31,250 and less than, greater than sign, 58 00:02:31,250 --> 00:02:35,050 and I'm going to put String, then parentheses on the end. 59 00:02:35,050 --> 00:02:38,560 Then on the next line, get, parentheses, 60 00:02:38,560 --> 00:02:40,520 equals operation. 61 00:02:40,520 --> 00:02:41,980 And of course, it shouldn't have been parentheses 62 00:02:41,980 --> 00:02:44,350 on the end of the string operation line. 63 00:02:44,350 --> 00:02:46,980 Alright, so at the moment, we've added two public fields, 64 00:02:46,980 --> 00:02:49,940 with a gather that thus returns the corresponding 65 00:02:49,940 --> 00:02:52,290 mutable live data objects, 66 00:02:52,290 --> 00:02:55,120 because the top of these fields is live data, 67 00:02:55,120 --> 00:02:57,680 the owner won't be able to use the value 68 00:02:57,680 --> 00:02:59,720 and post value attributes. 69 00:02:59,720 --> 00:03:01,930 The owner will see live data objects, 70 00:03:01,930 --> 00:03:03,960 not mutable live data objects. 71 00:03:03,960 --> 00:03:06,890 And again, live data objects don't have the value 72 00:03:06,890 --> 00:03:09,280 of post value attributes. 73 00:03:09,280 --> 00:03:12,140 Mutable live data is a subclass of live data, 74 00:03:12,140 --> 00:03:14,010 which how we're able to do this. 75 00:03:14,010 --> 00:03:16,580 Right, so we go back to MainActivity, check that out. 76 00:03:16,580 --> 00:03:19,720 We can see now that two of the errors have been fixed, 77 00:03:19,720 --> 00:03:21,920 and I've left result to last, 78 00:03:21,920 --> 00:03:24,700 because we're gonna be doing that one slightly differently. 79 00:03:24,700 --> 00:03:28,020 So we go back to our Calculator View Model, 80 00:03:28,020 --> 00:03:29,230 and specifically down here 81 00:03:29,230 --> 00:03:32,570 to the perform operation function. 82 00:03:32,570 --> 00:03:37,160 Notice that it's using toString function on line 85 83 00:03:37,160 --> 00:03:39,060 to convert operand1 to a string 84 00:03:39,060 --> 00:03:41,350 before storing it in result. 85 00:03:41,350 --> 00:03:43,690 Now, it would be more convenient to stick with a double 86 00:03:43,690 --> 00:03:46,207 inside the view model when we're dealing with the operand 87 00:03:46,207 --> 00:03:47,660 and the result. 88 00:03:47,660 --> 00:03:51,610 So what I'm going to do is remove the toString call first. 89 00:03:51,610 --> 00:03:54,660 Delete that out, and that's gonna cause an error of course, 90 00:03:54,660 --> 00:03:56,490 but we'll fix that next. 91 00:03:56,490 --> 00:03:58,000 I'm surprised that we've got an error there, 92 00:03:58,000 --> 00:04:00,150 cause that's expecting a string. 93 00:04:00,150 --> 00:04:03,570 The error's because result is a lot of data holding a string 94 00:04:03,570 --> 00:04:05,410 and we're assigning a double to it. 95 00:04:05,410 --> 00:04:07,860 We can fix that by changing the declaration of result 96 00:04:07,860 --> 00:04:09,590 on top of the class. 97 00:04:09,590 --> 00:04:11,378 Let's go back and do that. 98 00:04:11,378 --> 00:04:13,939 So we change result from a string here 99 00:04:13,939 --> 00:04:15,113 to a double. 100 00:04:17,720 --> 00:04:20,779 You can see then that typing it down perform operation, 101 00:04:20,779 --> 00:04:22,500 the error's cleared, and we've got a green tick 102 00:04:22,500 --> 00:04:24,300 on the top right hand corner of the screen. 103 00:04:24,300 --> 00:04:26,520 Indicating that everything's okay. 104 00:04:26,520 --> 00:04:28,540 The problem we now got though is that result 105 00:04:28,540 --> 00:04:31,910 solves a double, but we want to send a string to the owner. 106 00:04:31,910 --> 00:04:35,230 Now, this is extremely common; your mutable live data 107 00:04:35,230 --> 00:04:38,480 stores objects of one type, but the owner really needs 108 00:04:38,480 --> 00:04:40,780 to be observing a different type. 109 00:04:40,780 --> 00:04:43,710 In fact, it's so common that the last cycle components 110 00:04:43,710 --> 00:04:45,820 have a transformations class 111 00:04:45,820 --> 00:04:47,840 to deal with situations like this. 112 00:04:47,840 --> 00:04:49,833 So what we're gonna do is add the public attribute first, 113 00:04:49,833 --> 00:04:52,320 then we'll look at how to change its type. 114 00:04:52,320 --> 00:04:53,920 I'm gonna put this under result, 115 00:04:53,920 --> 00:04:55,590 the result definition. 116 00:04:55,590 --> 00:04:57,873 Val, stringResult. 117 00:04:57,873 --> 00:05:00,698 Colon, live data, 118 00:05:00,698 --> 00:05:02,260 and less than and greater than signs again. 119 00:05:02,260 --> 00:05:03,093 String, 120 00:05:05,170 --> 00:05:08,773 and on the next line, get, parentheses, equals result. 121 00:05:10,440 --> 00:05:13,240 Now, we've got an error, if we hover over that, we can see. 122 00:05:13,240 --> 00:05:14,420 Because we're attempting at the moment 123 00:05:14,420 --> 00:05:17,500 to assign a live data double to a variable that we have to 124 00:05:17,500 --> 00:05:20,720 clear as live data string. 125 00:05:20,720 --> 00:05:23,760 What we need is somewhere to transform the double live data 126 00:05:23,760 --> 00:05:25,360 into a string one, 127 00:05:25,360 --> 00:05:27,247 and that's where this transformations class 128 00:05:27,247 --> 00:05:29,690 that I'm about to show you comes into play. 129 00:05:29,690 --> 00:05:32,840 So, instead of putting get, parentheses, equals result, 130 00:05:32,840 --> 00:05:34,670 we're gonna put get, parentheses, equals, 131 00:05:34,670 --> 00:05:37,350 and it's gonna be transformations, capital t, 132 00:05:37,350 --> 00:05:41,620 Transformations, dot map, and then parentheses, 133 00:05:41,620 --> 00:05:45,540 we're gonna add result as the first argument, comma, 134 00:05:45,540 --> 00:05:48,670 and we're gonna add left and right curly braces. 135 00:05:48,670 --> 00:05:50,190 Then within those left and right curly braces 136 00:05:50,190 --> 00:05:54,350 we're gonna type it dot, toString, parentheses, 137 00:05:54,350 --> 00:05:56,070 and outside of the right curly brace, 138 00:05:56,070 --> 00:05:58,563 we're gonna add a closing, right parentheses. 139 00:05:59,399 --> 00:06:02,010 Right, and if we click on map, we'll then check that out, 140 00:06:02,010 --> 00:06:03,380 see what it does. 141 00:06:03,380 --> 00:06:06,500 You can see that it applies a function to each value 142 00:06:06,500 --> 00:06:08,720 emitted by the source live data, 143 00:06:08,720 --> 00:06:10,570 so that's result in our case, 144 00:06:10,570 --> 00:06:13,550 and then the result then returns into live data 145 00:06:13,550 --> 00:06:16,570 that emits the modified values. 146 00:06:16,570 --> 00:06:17,700 I'm closing that down again. 147 00:06:17,700 --> 00:06:20,840 Our function's really simple, it's just the lambda 148 00:06:20,840 --> 00:06:23,830 that returns to toString value of the double. 149 00:06:23,830 --> 00:06:26,120 So we'll now move the call toString 150 00:06:26,120 --> 00:06:28,210 from the perform operation function 151 00:06:28,210 --> 00:06:29,860 to the point where it's needed, 152 00:06:29,860 --> 00:06:31,690 providing a string to the owner. 153 00:06:31,690 --> 00:06:34,410 So this allows the view model to work with types 154 00:06:34,410 --> 00:06:37,140 that are most convenient and only convert them 155 00:06:37,140 --> 00:06:40,370 at the final stage where they're going to be observed. 156 00:06:40,370 --> 00:06:43,093 We didn't do that with the new number object, 157 00:06:44,000 --> 00:06:46,660 making it a double in other words, because we're receiving 158 00:06:46,660 --> 00:06:49,880 characters in digitPressed in that function. 159 00:06:49,880 --> 00:06:52,590 It's therefore much easier to build up new number 160 00:06:52,590 --> 00:06:54,920 by appending the next digital string, 161 00:06:54,920 --> 00:06:56,820 and it's a result I think is more convenient 162 00:06:56,820 --> 00:06:59,010 to keep new number as a string. 163 00:06:59,010 --> 00:07:01,530 You may however decide to code this differently, 164 00:07:01,530 --> 00:07:03,940 and have the activity use a single function. 165 00:07:03,940 --> 00:07:06,100 It could have for example passed both the number 166 00:07:06,100 --> 00:07:08,280 from its inner text, and the operation 167 00:07:08,280 --> 00:07:09,870 in a single function call. 168 00:07:09,870 --> 00:07:12,640 So, in that case, the digital buttons wouldn't call 169 00:07:12,640 --> 00:07:14,720 a function in the live data class. 170 00:07:14,720 --> 00:07:18,200 That just put the characters into the inner text wigeon. 171 00:07:18,200 --> 00:07:20,130 Typing one of the operator buttons will then call 172 00:07:20,130 --> 00:07:22,650 a live data function, then pass in the number 173 00:07:22,650 --> 00:07:25,200 from the inner text wigeon in the operation. 174 00:07:25,200 --> 00:07:27,850 Main activity would still only be concerned with UI, 175 00:07:27,850 --> 00:07:29,000 so that's fine. 176 00:07:29,000 --> 00:07:31,960 There's often many ways to organise the code, 177 00:07:31,960 --> 00:07:32,793 I wanted to show you 178 00:07:32,793 --> 00:07:35,840 how to observe simple live data objects at once, 179 00:07:35,840 --> 00:07:38,360 but that doesn't mean that you have to do it that way. 180 00:07:38,360 --> 00:07:40,650 If you wanna see what the alternative implementations 181 00:07:40,650 --> 00:07:43,770 might look like, I've added calculator two 182 00:07:43,770 --> 00:07:45,570 to the resources for this video. 183 00:07:45,570 --> 00:07:47,397 Or, have a go yourself first, 184 00:07:47,397 --> 00:07:49,810 and then compare your result to mine. 185 00:07:49,810 --> 00:07:51,420 And just before I finish the video, there is still 186 00:07:51,420 --> 00:07:54,830 one minor change that we can make here. 187 00:07:54,830 --> 00:07:57,050 You can see that we've got warnings over here, 188 00:07:57,050 --> 00:08:00,450 and have a look on line 20, we can see that it's 189 00:08:00,450 --> 00:08:01,890 underlined in grey. 190 00:08:01,890 --> 00:08:04,100 And when we move our mouse, there you can see it set up, 191 00:08:04,100 --> 00:08:07,160 android studio is was suggesting that the lambda argument 192 00:08:07,160 --> 00:08:09,490 should be moved out of parentheses. 193 00:08:09,490 --> 00:08:12,230 So click into there, we come over here 194 00:08:12,230 --> 00:08:13,710 and click on the light bulb, 195 00:08:13,710 --> 00:08:16,210 and take android studio's suggestion to move 196 00:08:16,210 --> 00:08:18,373 the lambda argument out of parentheses. 197 00:08:19,380 --> 00:08:21,163 That there clears the warning, and you can see now 198 00:08:21,163 --> 00:08:24,190 that we've got in the top right hand corner, 199 00:08:24,190 --> 00:08:25,890 we've got a green tick, 200 00:08:25,890 --> 00:08:27,480 so we're good to go. 201 00:08:27,480 --> 00:08:29,440 So I'll stop the video here now. 202 00:08:29,440 --> 00:08:31,410 In the next one, we're gonna see another advantage 203 00:08:31,410 --> 00:08:35,140 of completely separating the UI from the program's logic. 204 00:08:35,140 --> 00:08:36,440 See you in the next video.