Using lazy in Kotlin to bind Android viewsLet’s build a concise and user-friendly view binding solutionAndrew LordBlockedUnblockFollowFollowingJan 29When working in the UI layer of an Android app, it won’t be long before we need to perform an operation upon one of our views.
In order to do this, we first need to retrieve it via findViewById.
Although using the API can seem simple, it does introduce a block of boilerplate to our Activities.
On top of this the code to bind all the views usually ends up in onCreate, completely separate from the view properties themselves.
????Now that Kotlin has entered the scene with new features we didn’t previously have access to it seems like the perfect time to look for a new way of doing things.
We will explore some different approaches, before looking in detail at how lazy initialisation in Kotlin can be used to solve this problem.
Lazy can be goodThe situation in JavaTo set the scene and to demonstrate how Kotlin can help we first need to look at how these tasks are achieved in Java.
To bind views to a field, we would use findViewById, historically casting the resulting view to the correct subclass.
For an activity, the views would commonly be bound within onCreate, however, this may be elsewhere if views are retrieved at another time.
The example here is very simple, containing only two views, whereas in reality many different views may need to be accessed.
To avoid this repetitive boilerplate Butter Knife was created by Jake Wharton, which uses annotation processing to aid the binding of views.
????Adoption of KotlinThe Kotlin programming language has many new features and so it feels natural that it may offer a better solution to what we were using before.
The first option we will likely come across are the Kotlin Android Extensions provided as a plugin to the Kotlin language.
The plugin automatically generates properties that match the IDs of the views in our layout file.
Views no longer need to be stored manually as properties, as they are instead accessed via synthetic properties generated by the plugin.
The approach is dependent on a Gradle plugin and as with all generated code there will likely be occasional issues that require a Gradle sync to get things working again.
Whether Kotlin Android Extensions is the approach to take may come down to personal preference as thanks to the power of Kotlin there is another really nice option that isn’t dependent on generated code.
As with any potential solution it is best to try it out, see how it works and whether it fits with all the use cases a particular project has.
????Lazy bindingWhen initialising properties there is a feature of Kotlin that allows the getter and setter to be delegated, enabling capabilities such as lazy initialisation.
The idea is that on first access the provided function is used to initialise the property and future accesses will simply return it.
This is perfect for binding views, as the first access will call findViewById and then subsequent calls will simply returned the stored view.
Delegated properties are employed via the by keyword, with lazy being a global function that takes a lambda as an argument, making it very easy to apply.
By checking its signature we will find that lazy can also be provided with a LazyThreadSafetyMode argument, which by default will synchronise access to the lazy property to keep things thread safe.
As our lazily bound views will only be touched from the main thread, we can optimise performance and disable this behaviour.
Once this solution is applied across our app, the same lazy call will be made many times, opening up the possibility of extracting it out to a global function to keep things tidy.
????Bind viewIt turns out the only part of the lazy findViewById block that changes with each use, is the generic type.
The application of an Activity extension function can therefore allow the whole block to be reused each time we want to bind a view.
This leaves us with our final lazy view binding implementation.
There is a small matter of choice when it comes to assigning the view, the view type can either be specified on the property or as a generic constraint to bindView.
The best choice will be a matter of personal preference or specific to the project code style.
The sequence of steps we took have resulted in a succinct and clear call site.
We have full control over our view binding code and no generated code is required.
As our bindView function is essentially a wrapper around findViewById, the same extension could also be used to bind views elsewhere in the codebase beyond just in Activities.
????ConclusionBy making our view properties lazy delegated properties, we can strike a great balance between brevity, avoidance of boilerplate and reducing reliance on generated code.
Maintaining full control over how our views are bound can be really helpful for people trying to understand the code and for debugging it if something goes wrong.
The solution does require a little bit of infrastructure, however, pretty much all of the code is shared between each view binding, helping to keep the call site clean.
What is your preferred technique for accessing your views?.Do you use lazy delegated properties or do you think it is something you might try out?.Feel free to reach out to me on Twitter @lordcodes and let me know what you think or if you have any other feedback or questions.
If you like what you have read, please don’t hesitate to share the article and subscribe to my feed if you are interested.
Thanks for reading and happy coding!.????Check out the sample code for this article on GitHub.
The best place for reading the article is on Lord Codes, where you will get code with syntax highlighting and the best reading experience!.????.. More details