We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Leveraging Android Databinding with Kotlin

00:00

Formal Metadata

Title
Leveraging Android Databinding with Kotlin
Title of Series
Number of Parts
90
Author
License
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Android databinding is considered as both a powerful toolchain empowering your views with access to view data without the necessity to build cumbersome presenters and conversely as an overly complex convoluted mess of binding statements opening the door to unnecessary irresponsible domain logic in your view layouts. Whilst the latter of these statements can be true databinding offers a very powerful code generation syntax allowing you to utilise the power of the compiler to ensure that your binding statements are runtime safe. Combining this with the terse syntax afforded by Kotlin allows us to dramatically cut down on boilerplate and build complex user interfaces with relative ease. In this talk you can learn how to utilise extension bindings and property delegates with your layouts and manage your screen state transformations with true and safe two-way binding allowing you to build a fully reactive observable view layout.
Android (robot)Video gameSystem callData miningMereologyAndroid (robot)Keyboard shortcutConnectivity (graph theory)Source codeLibrary (computing)Boilerplate (text)Presentation of a groupBeta functionPoint (geometry)Point cloudLogicCartesian coordinate systemComputer programmingDeclarative programmingComputer animation
Android (robot)Social classMultiplication signAndroid (robot)Software bugKeyboard shortcutLibrary (computing)Cartesian coordinate systemRevision controlPlug-in (computing)LogicCodeBuildingProcess (computing)Electric generatorAbsolute valuePoint (geometry)CompilerBitData miningComputer animation
Android (robot)Keyboard shortcutVideo gameData managementCycle (graph theory)CollineationDescriptive statisticsUtility softwarePlastikkarteSoftware frameworkQuicksortCategory of beingPoint (geometry)Revision controlProgram codeData miningPlug-in (computing)Keyboard shortcutPay televisionContext awarenessView (database)Social classAndroid (robot)Computer animation
Android (robot)Category of beingTemplate (C++)Computer-assisted translationMultiplication signCodeSampling (statistics)Different (Kate Ryan album)Computer animation
String (computer science)Android (robot)Category of beingKeyboard shortcutField extensionFunction (mathematics)Multiplication signCategory of beingReading (process)Sampling (statistics)Interface (computing)WritingFunctional (mathematics)Variable (mathematics)Social classArithmetic meanImplementationExtension (kinesiology)CodeNeuroinformatikRight angleMessage passingGoogolState observerComputer architectureConnectivity (graph theory)Computer animation
Android (robot)Component-based software engineeringArchitectureProgramming paradigmQuicksortConnectivity (graph theory)Endliche ModelltheorieCartesian coordinate systemSoftware frameworkView (database)Computer architectureProjective planePrice indexDampingComputer animation
Data modelAndroid (robot)DampingPrice indexEndliche ModelltheorieComputer architectureKeyboard shortcutCategory of beingView (database)Connectivity (graph theory)Form (programming)Data miningCycle (graph theory)Video gameComputer animation
Android (robot)BendingKeyboard shortcutData modelFunction (mathematics)Field extensionFactory (trading post)Service (economics)Internet service providerInteractive televisionEndliche ModelltheorieCompact spaceMobile appCodeInstance (computer science)Data storage deviceConnectivity (graph theory)Execution unitThermal expansionParameter (computer programming)View (database)Factory (trading post)Functional (mathematics)State observerWindows RegistryMathematicsMultilaterationData structureVideo gamePoint (geometry)Cycle (graph theory)State of matterData miningSource codeExpressionConstructor (object-oriented programming)Field (computer science)Sign (mathematics)Category of beingInterface (computing)Extension (kinesiology)Sinc functionWrapper (data mining)Type theoryPrimitive (album)BitDefault (computer science)Event horizonKeyboard shortcutAndroid (robot)Computer animation
Android (robot)Field extensionFunction (mathematics)Regulärer Ausdruck <Textverarbeitung>Connectivity (graph theory)Keyboard shortcutState observerOperator (mathematics)BitElectronic mailing listService (economics)Position operatorProper mapString (computer science)Functional (mathematics)Transformation (genetics)State of matterCodeExpressionLevel (video gaming)Cycle (graph theory)Video gameGame controllerMathematicsMultiplication signBoolean algebraArithmetic progressionEscape characterSocial classAdditionEndliche ModelltheorieResultantLogicExtension (kinesiology)Complex (psychology)Category of beingMereologyMappingHash functionQuery languageView (database)Thread (computing)Repository (publishing)Fluid staticsThree-valued logicComputer animation
Android (robot)Complex (psychology)LogicVirtual machineBitEndliche ModelltheorieResultantView (database)State of matterCategory of beingComputer animation
Android (robot)Endliche ModelltheorieResultantState of matterView (database)BitCategory of beingSoftware developerExpressionDifferent (Kate Ryan album)Latent heatComputer animation
Android (robot)BitLatent heatState of matterAdditionAdaptive behaviorLogicFunctional (mathematics)Category of beingOperator (mathematics)Computer animation
Android (robot)Field extensionCategory of beingExtension (kinesiology)Operator (mathematics)View (database)Library (computing)Android (robot)Revision controlFunctional (mathematics)Mobile appCategory of beingExecution unitNamespaceCompilerString (computer science)Computer animation
Android (robot)Field (computer science)Category of beingWindows RegistryMathematicsIdentifiabilityExtreme programmingCodeDoubling the cubeDimensional analysisMeeting/InterviewComputer animation
Android (robot)IdentifiabilityAndroid (robot)CodeEndliche ModelltheorieInterface (computing)Windows RegistryMathematicsField (computer science)State observerCategory of beingNormal (geometry)ImplementationView (database)Meeting/InterviewComputer animation
Android (robot)MathematicsState observerEndliche ModelltheorieParameter (computer programming)Category of beingProgrammable read-only memoryFunctional (mathematics)BitField (computer science)View (database)ImplementationSet (mathematics)Boilerplate (text)Computer-assisted translationFamilyQuicksortReflection (mathematics)Keyboard shortcutComputer animation
Android (robot)Category of beingField extensionFunction (mathematics)Binomial heapCategory of beingTemplate (C++)State observerSocial classMathematicsRight angleIdentifiabilityReflection (mathematics)CoprocessorFunction (mathematics)Link (knot theory)Computer fileDependent and independent variablesKeyboard shortcutExtension (kinesiology)Functional (mathematics)BuildingCodeField (computer science)Stack (abstract data type)ProgrammschleifeAndroid (robot)Buffer overflowProcess (computing)System callLimit (category theory)Eccentricity (mathematics)Term (mathematics)Structural loadDifferent (Kate Ryan album)GoogolDataflowDrop (liquid)View (database)Constructor (object-oriented programming)Data miningSampling (statistics)Open sourceComputer animation
Android (robot)Sample (statistics)Sampling (statistics)Different (Kate Ryan album)Open sourceCategory of beingData storage deviceStructural loadField (computer science)Slide ruleLambda calculusDoubling the cubeMusical ensembleTask (computing)Video gameComputer animation
Android (robot)Type theoryAdaptive behaviorBlogSource codeStreaming mediaConsistencyElectronic mailing listCategory of beingPosition operatorDomain nameKeyboard shortcutRepository (publishing)Inverse elementOpen sourceComplex (psychology)Perfect groupView (database)FlagEndliche ModelltheorieMereologyBoolean algebraPoint (geometry)Interior (topology)Absolute valueCodeSampling (statistics)CASE <Informatik>Functional (mathematics)Level (video gaming)Projective planeResultantLibrary (computing)Presentation of a groupWebsiteClient (computing)State of matterProcess (computing)Game controllerVarianceDifferenz <Mathematik>MathematicsBitUtility softwareFile formatAvatar (2009 film)TowerVideo gameLimit (category theory)Machine visionData miningSystem callCycle (graph theory)HypermediaRepresentation (politics)Right angleKey (cryptography)ChainEntire functionComputer animation
Keyboard shortcutSample (statistics)Android (robot)Thread (computing)CodeSingle-precision floating-point formatField (computer science)Differenz <Mathematik>Computer animation
Transcript: English(auto-generated)
So I'm Ash Davis. I'm working with Immobile InScout, and I'm talking about leveraging Android data binding with Kotlin. Now, there's a lot of topics to get into here, and I've only managed to cover a partial part. But I really encourage you to check these out. So let's start off. What is data binding? Data binding is a support library
that allows us to bind UI components to data sources declaratively rather than programmatically. It's potentially really powerful and complex, but used effectively, it can really cut down on presentation boilerplate. So it started off with data binding beta in 2015 when Google announced it after IO. It's a support library that can be used right back down
to Android 2.1, which is API version 7, to write declarative layouts and minimize the glue code required to use your application logic and layouts. As of Android Gradle plugin 3.1, it includes the new data binding compiler, so data binding version 2. This includes incremental class generation,
so this means it will speed up your build times a little bit. And it will have pre-compilation of classes, which means that your data binding classes will actually be available prior to your first build. If you've used any annotation processes that generate classes that you rely on at runtime, you'll know this is an absolute pain. So it's nice to have these even if your build bug is up.
So let's start off with a basic activity and see how we can start using data binding with this. This is just a basic activity, how it might start off with. So first, we want to make sure we replace the framework set content view with that of the data binding utility to retrieve a binding class that has been generated for us that we can refer to in our programming code.
Also, as of the Android Gradle plugin version 3.1, data binding is lifecycle aware. This means we need to inform the binding of our lifecycle owner so it can manage the subscription accordingly. So this is already pretty basic. So how can we use Kotlin to improve this? Let's start off with the binding.
So Kotlin allows us to use delegated properties, this business cat delegates. So with delegate properties, we can create a getter and set a template to define how we want to use this resource. And we can reuse it as many times we'd like in different places as long as it makes sense. So for example, delegated property
would be our Kotlin lazy delegate. Here you can see the sample from the documentation where computed would be referred only once. And then the hello value would actually be referred every time we then come to access it. So Kotlin gives us two interfaces to use when we want to create our own delegate properties.
We have the read only property and the read write property. These are for value properties and for variable properties respectively. So one is immutable and one is mutable. And we have to implement these methods to actually provide the functionality. So for our delegate property for our activity binding,
we want to make sure that we're actually using a immutable property, so the read write property. Here we have just a basic class. I want to add our implementation to this. So you can see that the receiver is the activity, meaning that for this property delegate, it has to be made from an activity so we can use the reference activity. We pass in the resource ID for the layer.
And we want to lazily instantiate our binding so that we'll refer to the same one in the same way that we would use with lazy. How does this look like in our activity? We can use an extension function as well to access our delegate property. This is consistent with how we do so with a lot of other Kotlin delegate properties like lazy or observable, so that it looks quite nice
and consistent in our code. So let's introduce some architecture in here so we can actually get some business behavior. In last year's Google I.O., Google announced the architecture components to start building framework applications quickly and easily. And again, this year's Google I.O.,
they announced Jetpack components so we can get even more in there. This includes live data and view models and a sort of unofficial endorsement of using model view, view model as a paradigm. So it wasn't an instruction to migrate your existing architecture over from MVP or from MVI
or from whatever you have to MVP, MVVM, sorry, but it was an indication that this is what you can use if you've got a fresh project or if you're starting from scratch. But data binding works really well with MVVM because the view passively observes the view model and it doesn't really matter if the view is there or not. So we don't have to worry about unregistering a listener
or making sure our view's nullable or having all these because we can have many or no listeners on our view model, view model properties. And the lifecycle of our components react accordingly. So the view model state is bound by view observation and the view model notifies, so the view notifies the view model of UI events for user interactions. I could probably talk all day about how MVVM works
but this talk is about data binding. So let's move on. So this is our activity as it was before and we need to actually apply our model to our binding so we can access it from our view and actually start writing binding expressions. So we access the view model through use
of the view model providers of the app compact activity and we provide a factory to instantiate the view model if it's not already been created. You have to make sure to do this in onCreate because otherwise the activity will not have a chance to actually restore its view model store which it retrieves from its non-configuration instance. If you're interested about how this works, I recommend checking out the source code
of the app compact activity but it's a bit messy as Android code is. So how can we improve this already? We can introduce an extension function, I love extension functions, to actually retrieve the view model with a factory. You notice here that I'm using the view model new instance factory. This is a default factory that will try
and actually instantiate the view model if it has no argument constructor. It's generally not safe to actually rely on this so I would advise not using it but it's there if you can assert that you have no arguments in your constructor.
Additionally, if you want to access a property on your model later on or in another place in your activity, you can use the lazy delegate property to instantiate the view model inside and then assign that to the binding. Then you can access that later without having to worry about the nullability of your binding model since the generated code will have the nullable annotation
since it's technically nullable. So let's look at our view model. This is a basic implementation, a basic thing and we have the observable field. Now data binding provides observable fields that we can use as a kind of wrapper to notify data binding of a property change.
There are a lot of primitive types like observable Boolean, observable byte, observable char, short int, long, passable but mostly I want to use observable field. Observable interface allows us to add and remove listeners to notify us of a property change
and this extends the base observable and implements it using the property change registry. So we can extend from this if we have a complex binding structure and this will be important later. So as mentioned, as of 3.1, we can use lifecycle-aware components with live data, sorry, with data binding so we can actually have our live data
on our data binding as a lifecycle observer. So we're creating mutable live data because we want to modify the value. It's still a value but it's mutable so it's the same as using a mutable list or just a list of and the nice thing about this is that
because mutable live data is lifecycle-aware, you have a bit more granular control over how you want to behave to notifications and changes in your data and also provide some additional benefits that you might be aware of using transformations, switch mapping and lifecycle owner observation. You can also use an extension function
to instantiate this in a similar way that you might instantiate a mutable list of or list of or hash map of as you would do with Kotlin. It's just the little things that make your code a little bit more readable and more concise and consistent. So let's go back to the view model and let's introduce some asynchronous operation. We've got a service and we fetch a list of repositories
from the service, for example, GitHub. We'll just assume that they're strings and we'll just assume that there's proper asynchronous threading going on here. It's not important. Say you want to show a progress bar to the user to let them know that there's something going on in the background so we don't have a static state,
a static view, sorry, and then we want to have a progress bar disappear and appear as it's when corrected. So introduce a loading live data that we can observe and we set the value to true and false before and after making the request. So how do we do this in our layout expressions? In our layout expression we've got
a binding to our model dot loading and we want to make sure it's visible or gone. So the problem is we have a Boolean which is like a two state, whereas we want to map this to a three state view visibility property. It's fine doing like this for now, but we can see how we can improve it in the future. So the progress bar visibility will be updated
as and when this value changes because the generated data binding class will listen to the changes respectfully. We also have to import the view so that it knows to use view dot visible. It's not clever enough to figure that out on its own. So what if we wanted to display an empty state in addition to our progress state? So say to indicate to the user
that their query didn't return any results or we just want to have a default empty state. So introduce a text view, but we also want to make sure this is only visible when there are no items in our model. So we have this. But we also make sure that our loading state isn't conflicting with this. So we don't want the empty to actually appear whilst our loading progress bar is visible.
So we also combine this with the loading property and to have a compound ternary expression here using the ampersand, which of course has to be that escape because it's XML and everyone loves XML. So you can already see what's going wrong here. It's too complicated.
We're polluting our layout with business logic. It's not very easily testable and it's really putting people off. It's often one of the problems that I hear from people about wanting to use data binding because they're worried about having complex business logic in their layouts and it's discouraging and it doesn't need to be.
I'm really proud of this GIF. Rube Goldberg machines are awesome. So how can we alleviate this? We can then create an additional property in our view model so that it represent the empty state. Now, it's only modifying the empty state based upon the result of the model. So it's a bit duplicitous
because it's the same as what was happening in our view. But because it's happening in our view model, it means it's much more easily testable. So in our layout, we now have a bit of a cleaner expression. Now this is better, but we're a couple of developers. We can be better than better. We can be awesome. So let's introduce binding adapters.
Binding adapters allow us to create an additional bit of specification to decide whether or not something should be bound or how it should be bound. So as I mentioned earlier, when we have a Boolean two state we can then decide how to map that to a visibility three state. So whether to use invisible, visible, or gone. So it provides additional logic.
So we can create a binding adapter with gone and less property and then decide if visible, view visible, or view gone. So we've moved this conditional operator that we had in our XML layout into an actual Kotlin testable function. And furthermore, we can actually make use of Kotlin extension functions.
So this is an example of an extension function available to us in the Android KTX library. So we can actually access the visibility of a view based upon whether it's visible or gone. So this is accessible both programmatically and in our declarative layouts. Unfortunately, the version in the KTX library
doesn't have this annotation, but you can define your own. So now our layouts are looking much more healthy. I think it's much more clear what's going on. And we don't have to worry too much about testing because if this is incorrect or if it's binding to the wrong property, if you say, for example, loading is actually a string instead of a Boolean, then it won't compile.
And we're leaning on the compiler instead of relying on unit tests. So it's much more safer and much cleaner way of doing it. Don't forget to actually use the app namespace instead of Android because we're having our own custom property. So XML looks good now, but we can improve the VM.
Introducing bindable, also a great GIF. Remember how I said observable fields extends from base observable? Now, this does so by actually creating a property change registry. And our bindable annotation is applied to the properties in our generated binding class, which means it generates a br resource identifier
similar to our resource identifiers that you'll have in Android.R. So you can access like strings, dimensions, and layouts, and you can also access bindable resources from your code. So we can create an observable view model. An observable view model will just be a normal view model that extends from the observable interface
and uses the implementation from the property change registry to let the observers know that something has changed. So we can either let it know that everything's changed or just in an individual field. So what might that look like? Sort of a simple observable view model implementation here. You can see the two methods,
add on property change callback and remove on property change callback will actually allow data binding to then listen to various properties. We've added two functions as well where we're notifying the change and notifying the property changed. Now, it's calling the same method here, but it's referencing the different parameters. So using the parameter zero means that all properties have changed and you need to rebind your entire dirty layout
or just specifying an individual field to let data binding know that one field's changed, which is a little bit more concise. So in our view model, we now extend from the newly created observable view model and we can start creating bindable properties. So instead of using mutable live data, we can create an actual implementation.
So we have variable items. So now we have variable, which can be changed and it's much more reflective of its mutable status. And we have the notify property change to let us know the data binding listeners that this property has changed upon set. What's really useful here as well is that the setter is private,
which means we can prevent any access from outside the view model and have it really localized. But it's still a little bit verbose. There's still a little bit going on there. So we can remove this boilerplate. We can do better. Remember our delegate cat? We can create delegate properties again in Kotlin
to define getters and setters as templates to reuse. So we can do so with an observable property. So we can condense this getter and setter into just a property value. And this is what it might look like. We want to extend the observable property in Kotlin so that we can actually listen for changes on this class
so that we can react accordingly to before and after the property changes. Now, this observable property overrides two parameters, two functions, sorry. Before change and after change. Before change takes a Boolean response. So this is to indicate whether I approve of this change or whether I'm actually allowing this change to happen.
So for this, we can actually then say, I only want to change this if the old value does not equal the new value. This means that we're not unnecessarily notifying the data binding of changes. And if you're using two-way binding, it means you can actually prevent infinite loops. And if you've ever had to Google stack overflow on stack overflow, you'll know how difficult that is.
It's not easy. So we've also got after change, which actually does the notification job. So it's letting the observable know that this property has changed. And you can see in our constructor that we're taking in the initial value, the observable to notify, and the identifier of the fields. Now, the identifier of the fields will be this property
from the BR class that I mentioned earlier. Cool. So we can apply this with an extension function. I've got a lot of extension functions. You don't want to see my code. So we, those guys are laughing because they know. So we've got the annotation get bindable on our items
to make sure that this bindable annotation is applied to the getter. So let data binding know this property will change or might change. And then we're passing in the identifier br.items which is generated by our data binding annotation processor. The annotation processor, by the way, does not need to be added as a capped or annotation processor in your build.gradle.
It will be done so by the Android gradle plugin. Cool. So there's a really nice article from Adam McWilliams actually who demonstrates this technique and builds upon it a little bit further by using reflection to then check out the br.items file. So it doesn't really pollute your stack trace if you have a problem at compile time.
I chose not to demonstrate that here so I recommend you please check it out. The links will be online. So awesome. We've got lots of different techniques like this that can be applied in Kotlin and data binding. And unfortunately I can't go into great detail about them all. But I've created a sample online which you can access on GitHub. It's all open source and it's available on the Play Store.
It's got loads of five-star reviews. By loads I mean none. So review it, you know, please. You can see lots of different techniques that I've used here to demonstrate how you can bind properties. So you can see the bindable property that I mentioned earlier. You can see the observable field. You can see mutable live data.
And you can also see how you can use mediator live data to combine the value of two mutable live datas and actually operate on a lambda on this to then have some custom behavior. It's, you can really achieve a lot of things and you can do so in a nice testable fashion so that your declarative layouts are nice and clean. And finally, these slides are available online
in open source markdown formats because open source all the things. So you'll be able to check out all the additional resources that I've used as well. I recommend go check it out. There's a few blog posts on there on how to use data binding with RecyclerView so you can actually use just one adapter. I think it was from George Mount, I think.
And the aforementioned article from Adam McWilliams. So thanks. So we're running early, nearly the whole day. Questions? Oh wow, great.
Of course you'd have a question. Hi. Hey, thanks for the great presentation. I think like Kotlin delegated properties are great and really powerful. But something I noticed, you first used live data but then in the Kotlin delegated property you didn't.
So can you quickly compare those two? Well, so live data will have obviously a lot more granular control over what's happening in the lifecycle. And you can choose to use either or whichever fits for your project. So you can have binable properties because it actually represents the mutable state because it's a var instead of a val. And then you can have mutable live data as a live data
and as mentioned as well, you can use mediator live data to combine the results of other functions or do some complex internal logic if you need to. So it really is just dependent on what the needs of your projects are. Okay, we have another question, right? Hi, so good presentation. I don't know, but I work for a library
and I use data binding and I'm having issues because now my clients need to activate the data binding in their site. Is this how it works? Is it possible to change it? Because I know that the generated code is provided inside my library.
But even though they need to activate data binding to make a mapper or something. I would think that the, because annotation processing needs to be done for this and this is done on the top level. So unless you're actually using, unless you're actually having the generated layouts in your actual library, you probably have to definitely need it in the consuming project. I've not actually had this use case myself,
so I couldn't say accurately, but I think, I'm not sure there's a way around it. Any further questions? Oh, perfect. Thank you, really great, great presentation. Thank you very much. So my question, I don't really get the part
when you change the view and the flag visibility for the view. So how did you do that? Could you please repeat quickly, thank you. So in the view model, there's a observable Boolean and whether you use live data or a bindable property, it's not too important.
But data binding will listen to this value. So normally you wouldn't be able to directly map a Boolean to a visibility int, which would be like view.visible, view.invisible. And that's the point of using a binding adapter because in the binding adapter you can say, I take this Boolean and I'm turning it into the visibility int. So that's telling data binding how you want to react to this binding property.
And you can use your own type for it? Absolutely. Oh, that's good. It's actually really powerful in this respect because you can create custom views, you can create custom binding methods and binding adapters, inverse binding adapters, inverse binding methods. It's really Pandora's box, but it's massively flexible and really powerful. I've actually created a few custom views myself
with inverse binding adapters so then you can bind your custom view types from your view model in your XML layout. So it's still nice and clean, but you can have really complex logic or as complex as your domain needs it to be. Thank you. Thank you very much. No worries. And there's one last question. I already use data binding quite extensively.
I have one question. I mean, I got one challenge with actually pushing the, some of the view model is connected to the data source and basically the data source is providing a list of data or collections. So the challenge is with having the collections pushed
like through the stream when only one item from data source changes, but it's all the whole collection is pushed. So I still didn't find a nice solution, like push just to the item that changed at a certain position and things like that. Well, it depends on how your receivers are actually subscribing to this.
So in the sample that I've got online, I'm showing a list of repositories and a RecyclerView. And now because the adapter should still be in your activity implementation, what's happening in it is that the view model will then expose the entire list that's changed. And that's fine. And then it will be observed in your activity and the activity will then update the adapter. The adapter internally then has a diff util
that can say only one item has changed. So it can indicate to its bound view in the RecyclerView that only this item needs to be updated. So it's a bit of an easy way of doing it. I think if you, yeah, if you check out the code on there, you can see how it's done with a, I've used a single layout adapter with a single diff util, and it will only update the fields it needs to.
So you're doing the diff on the UI thread basically? Okay, thank you very much.