

Getting rid of boilerplate with Kotlin Android Extensions
source link: https://www.kotlindevelopment.com/kotlin_android_extensions_eliminate_findviewbyid/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Getting rid of boilerplate with Kotlin Android Extensions
Posted on 18 October 2017 by Andras Kindler
findViewById - probably the most common phrase in every Android codebase. And if you think about it, it's a bit of an overkill. After defining a view with an id and a class in xml, one has to look it up, and cast it, before starting to use it in code - the definition of boiler plate code. No wonder developers are trying to get rid of it with serious effort.
The purpose of Kotlin Android Extensions is to completely eliminate the phrase above. Basically after applying the plugin, every view can be referenced by their id supplied in the xml layouts, without casting, as their respective types. That's it.
How to use
The Android Extensions are part of the Kotlin plugin for Android studio, no install necessary. The first step is to apply the plugin in the build.gradle
script:
apply plugin: 'kotlin-android-extensions'
Then import every view in a layout with the following line:
import kotlinx.android.synthetic.main.<layout>.*
Where <layout>
is the file name of the xml layout. So for example the import statement for activity_login.xml would be import kotlinx.android.synthetic.main.activity_login.*
.
And now you can access each view by their ids supplied in the xml. For example after declaring the following Button in XML:
<Button
android:id="@+id/loginButton"
...
/>
It can be accessed in code like this, without any preceding ceremony:
loginButton.text = "Login"
That's all. The Kotlin Android Extensions can be used anywhere where xml layouts are involved: activities, fragments, custom view subclasses, viewholders, etc.
Under the hood
Kotlin Android Extensions is a compiler plugin that offers a convenient way of accessing views defined in XML via a property-like syntax. But even though the views look like real class properties in the Kotlin code, they are synthetic, meaning accessing them means calling a hidden method in the background.
After importing a layout, the compiler adds a caching function and a HashMap holding the views to the class. This guarantees lazy access: when a view is used in code, the caching function looks it up in the HashMap, and tries to add it to the cache if not present (by extracting the view with findViewById()
).
The decompiled bytecode contains a HashMap object for caching the views.
private HashMap _$_findViewCache;
Access is managed via the findCachedViewById()
function.
public View _$_findCachedViewById(int var1) {
if(this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
if(var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(Integer.valueOf(var1), var2);
}
return var2;
}
All of the above is hidden from the developer, everything happens automatically in the background. It is worth knowing that writing loginButton.text = "Login"
in Kotlin translates to the following statement:
((Button)this._$_findCachedViewById(id.loginButton)).setText((CharSequence)"Login");
Also, a second function is added to the class, for clearing the HashMap, to rebuild the cache for whatever reason. Also, when using the Kotlin Android Extensions with fragments, this is called automatically in onDestroyView()
, when the views are recreated but the fragment object stays intact, so the state of the cache becomes invalid. It can also be called manually with clearFindViewByIdCache()
:
public void _$_clearFindViewByIdCache() {
if(this._$_findViewCache != null) {
this._$_findViewCache.clear();
}
}
Fine-tuning the cache
It is possible to with the @ContainerOptions
annotation. The default setting uses a HashMap, which can be changed to a SparseArray (with the SPARSE_ARRAY
parameter), or turned off entirely (NO_CACHE
parameter).
An example:
@ContainerOptions(NO_CACHE)
class MainActivity : Activity() {
...
}
However, this is an experimental feature, the following flag has to be set to true in the build script:
androidExtensions {
experimental = true
}
Adapters and viewholders
While activities, fragments, and custom views work effortlessly, caching is not available outside those, so using the extensions with classes (adapters, viewholders) require a different approach. Basically what this means is after inflating a view, its subviews will be available in the same property-like way, but will not be cached. Taking a look at the decompiled bytecode reveals that every time a view is accessed, findViewById
is called - the cache and the helper methods are not generated in these cases. This can make the extensions very inefficient with adapters.
The first solution is to wrap the itemviews into a custom view, so caching will work this way. This is a preferred solution anyways, so the LOC count can be kept to a minimum in the adapter subclass.
The second approach is experimental, meaning the flag above has to be set to true in the build script before using it. When using the extensions outside of an activity, fragment, or custom view, implementing the LayoutContainer
interface will make caching available automatically. However, the container view will have to be passed as a constructor parameter.
Alternatives
The findViewById
problem inspired many to come up with clever solutions.
- Kotter Knife, the kotlinized version of Butter Knife uses annotations to inject views
- Google also improved the original method: casting is not necessary anymore, since it returns a
<T extends View>
type instead ofView
. - Or simply skipping XML in favor of building UI via code - the Anko Layout DSL is a nice candidate here.
Performance tradeoff
There's not much, really. Lazy access and caching guarantees efficiency, and the impact on the method count is subtle: every class is extended with two new functions.
Bottom line
We've been using the extensions in production for quite some time to our complete satisfaction, and we think currently this is the best solution for dealing with XML layouts. A more convenient way of accessing views, resulting in cleaner code, and zero performance drawbacks - there's no way we're going back to findViewById()
.
Recommend
-
18
The upcoming version of the C++ Standard (C++2a) is proposing to deprecate certain usages of the volatile keyword, by adopting the P1152 proposal ( Deprecating volatile ).
-
18
Getting Rid of Smart Quotes on OSX Feb 16, 2017 I don't know about any other developer but I bloody well hate smart quotes. I'm sorry but if I type in a "foo" mark then I expect it to remain "foo" not some ba...
-
12
Getting rid of virtual method calls Feb 11, 2007 • Andre Weissflog In Nebula2, platform abstraction was done through subclassing and virtual methods. For instance, the nGfxServer2 class implemented the platform-in...
-
20
Getting rid of array bound checks using ref-returns and .NET 5 Imagine we have a simple class, a wrapper around some array of structs (better data locality etc.): ...
-
15
Getting Rid Of A Living Nightmare In Testing — Smashing MagazineAbout The AuthorAfter her apprenticeship as an application developer, Ramona has been contributing to product development at
-
4
C++ coroutines: Getting rid of our reference countRaymond...
-
7
Get Rid of the Boilerplate in JAVA with Annotations @Lombok Reading Time: 4 minutesHi all, we have been programming in Java for quite a while now and came across various scenarios such as those concer...
-
5
React Native Universal Monorepo A React Native monorepo boilerplate supporting multiple platforms: Android, iOS, macOS, Windows, web, browser extension, Electron. This monorepo uses
-
8
How to Get Rid of the INotifyPropertyChanged Boilerplate.NET is a great platform to write user interfaces. From
-
9
@alex-omeyerAlex OmeyerCo-founder & CEO at stepsize.com, SaaS to measure & manage technical debtMany engineering teams get stuck and c...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK