(light music)
We've separated the logic of our app
from the user interface, the UI,
and our activity doesn't do anything
that isn't directly related to interacting with the user.
But I did mention that we shouldn't really be exposing
mutable live data objects because there's a risk
that the view model's owner could change them.
So in this video we're going to look at how to fix that.
If we don't want to expose the mutable live data object
then the first step is to mark them private
in Calculator View Model.
Now class, to do that we're going to do just that.
On line 16, private,
and we'll do the same for line 17 and 18
marking all three as private.
Once we've done that, we go back to main activity.
You can see now that in fact we break main activity
because it can't access the private attributes
of the view model.
What we're going to do though
is create new attributes that it can use.
So I'm gonna modify main activity to use these,
even though they haven't been created just yet.
So we'll be getting an error,
but what we'll do, instead of getting our result
It's gonna be string result,
instead of new number.
It's gonna be string new number.
Noticing I'm changing the capitalization there of the words,
and then string operation,
instead of operation.
And again, we've got errors,
but they'll go once we create the attributes
back in Calculator View Model.
We'll go and start looking at that now.
So for each of our mutable live data objects,
we need corresponding live data object
that the observer can use.
I'm gonna leave result to last, do that other two first.
So I'm gonna start, just making this space with new number,
set below the definition for new number,
we're going to type val stringNewNumber.
It's gonna colon, LiveData, instead of mutablelivedata.
Then, we can define that its a string, so left and right,
less than and greater than signs at the string there,
we're gonna accept that if an employer asks you to do that,
you can accept that,
and then on the next line,
we'll do a tab, and type get, in parentheses,
equals new number.
That's our first definition,
and let's do the same now for operation.
So below the definition of operation,
val string operation,
colon, live data,
and less than, greater than sign,
and I'm going to put String, then parentheses on the end.
Then on the next line, get, parentheses,
equals operation.
And of course, it shouldn't have been parentheses
on the end of the string operation line.
Alright, so at the moment, we've added two public fields,
with a gather that thus returns the corresponding
mutable live data objects,
because the top of these fields is live data,
the owner won't be able to use the value
and post value attributes.
The owner will see live data objects,
not mutable live data objects.
And again, live data objects don't have the value
of post value attributes.
Mutable live data is a subclass of live data,
which how we're able to do this.
Right, so we go back to MainActivity, check that out.
We can see now that two of the errors have been fixed,
and I've left result to last,
because we're gonna be doing that one slightly differently.
So we go back to our Calculator View Model,
and specifically down here
to the perform operation function.
Notice that it's using toString function on line 85
to convert operand1 to a string
before storing it in result.
Now, it would be more convenient to stick with a double
inside the view model when we're dealing with the operand
and the result.
So what I'm going to do is remove the toString call first.
Delete that out, and that's gonna cause an error of course,
but we'll fix that next.
I'm surprised that we've got an error there,
cause that's expecting a string.
The error's because result is a lot of data holding a string
and we're assigning a double to it.
We can fix that by changing the declaration of result
on top of the class.
Let's go back and do that.
So we change result from a string here
to a double.
You can see then that typing it down perform operation,
the error's cleared, and we've got a green tick
on the top right hand corner of the screen.
Indicating that everything's okay.
The problem we now got though is that result
solves a double, but we want to send a string to the owner.
Now, this is extremely common; your mutable live data
stores objects of one type, but the owner really needs
to be observing a different type.
In fact, it's so common that the last cycle components
have a transformations class
to deal with situations like this.
So what we're gonna do is add the public attribute first,
then we'll look at how to change its type.
I'm gonna put this under result,
the result definition.
Val, stringResult.
Colon, live data,
and less than and greater than signs again.
String,
and on the next line, get, parentheses, equals result.
Now, we've got an error, if we hover over that, we can see.
Because we're attempting at the moment
to assign a live data double to a variable that we have to
clear as live data string.
What we need is somewhere to transform the double live data
into a string one,
and that's where this transformations class
that I'm about to show you comes into play.
So, instead of putting get, parentheses, equals result,
we're gonna put get, parentheses, equals,
and it's gonna be transformations, capital t,
Transformations, dot map, and then parentheses,
we're gonna add result as the first argument, comma,
and we're gonna add left and right curly braces.
Then within those left and right curly braces
we're gonna type it dot, toString, parentheses,
and outside of the right curly brace,
we're gonna add a closing, right parentheses.
Right, and if we click on map, we'll then check that out,
see what it does.
You can see that it applies a function to each value
emitted by the source live data,
so that's result in our case,
and then the result then returns into live data
that emits the modified values.
I'm closing that down again.
Our function's really simple, it's just the lambda
that returns to toString value of the double.
So we'll now move the call toString
from the perform operation function
to the point where it's needed,
providing a string to the owner.
So this allows the view model to work with types
that are most convenient and only convert them
at the final stage where they're going to be observed.
We didn't do that with the new number object,
making it a double in other words, because we're receiving
characters in digitPressed in that function.
It's therefore much easier to build up new number
by appending the next digital string,
and it's a result I think is more convenient
to keep new number as a string.
You may however decide to code this differently,
and have the activity use a single function.
It could have for example passed both the number
from its inner text, and the operation
in a single function call.
So, in that case, the digital buttons wouldn't call
a function in the live data class.
That just put the characters into the inner text wigeon.
Typing one of the operator buttons will then call
a live data function, then pass in the number
from the inner text wigeon in the operation.
Main activity would still only be concerned with UI,
so that's fine.
There's often many ways to organise the code,
I wanted to show you
how to observe simple live data objects at once,
but that doesn't mean that you have to do it that way.
If you wanna see what the alternative implementations
might look like, I've added calculator two
to the resources for this video.
Or, have a go yourself first,
and then compare your result to mine.
And just before I finish the video, there is still
one minor change that we can make here.
You can see that we've got warnings over here,
and have a look on line 20, we can see that it's
underlined in grey.
And when we move our mouse, there you can see it set up,
android studio is was suggesting that the lambda argument
should be moved out of parentheses.
So click into there, we come over here
and click on the light bulb,
and take android studio's suggestion to move
the lambda argument out of parentheses.
That there clears the warning, and you can see now
that we've got in the top right hand corner,
we've got a green tick,
so we're good to go.
So I'll stop the video here now.
In the next one, we're gonna see another advantage
of completely separating the UI from the program's logic.
See you in the next video.