Migrating the deprecated Kotlin Android Extensions compiler plugin

In Kotlin 1.4.20-M2 JetBrains deprecated Kotlin Android Extensions compiler plugin.
Actually this was expected long time ago, in this commit you can see
kotlinx.android.synthetic is no longer a recommended practice. Removing in favour of explicit findViewById
But why?
There are some well-known issues with kotlinx synthetic
- They expose a global namespace of ids that’s unrelated to the layout that’s actually inflated with no checks against invalid lookups.
- They are Kotlin only
- They don’t expose nullability when views are only present in some configuration.
- All together, these issues cause the API to increase number of crashes for Android apps.
- Also Google is promoting modularisation but synthetic properties don’t work cross-module there is an open issue since January 2018.
What are the alternatives?
- View Binding is the recommendation for view lookup as well as binding, but it does add a bit of overhead when compared to Android Kotlin Extensions.
Compared to Kotlin Extensions, it adds compile time checking of view lookups and type safety. - findViewById the old traditional way that works for both Kotlin and Java.
JetBrains deprecated the Kotlin Android Extensions in favour of View Binding, so we will be exploring how to migrate to View Binding in this article.
View Binding
Not to be confused with Data Binding
View binding is a feature that allows you to more easily write code that interacts with views.
Once view binding is enabled in a module, it generates a binding class for each XML layout file present in that module.
An instance of a binding class contains direct references to all views that have an ID in the corresponding layout.
View Binding is Null-safe for layouts defined in multiple configurations.
View binding will detect if a view is only present in some configurations and create a @Nullable
property.
View Binding works with Java and Kotlin.
How to enable View Binding?
You don’t need to include any extra libraries to enable view binding. It’s built into the Android Gradle Plugin starting with the versions shipped in Android Studio 3.6. To enable the feature in the modules that will use it add the following into your build.gradle
file.
How to use View Binding?
If view binding is enabled for a module, a binding class is generated for each XML layout file that the module contains.
Each binding class contains references to the root view and all views that have an ID.
The name of the binding class is generated by converting the name of the XML file to Pascal case and adding the word Binding
to the end.
Use view binding in activities
Then you can access the views using the binding
object
binding.name.text = "Some Text"
Use view binding in fragments
Using view binding in fragments requires a bit more attention, as view binding doesn’t play well with fragments, as it introduces memory leaks, if you don’t nullify the view in OnDestroy
, then it’s not cleared from memory.
Then you can access the views using the binding
object the same way we do in activities.
binding.name.text = "Some Text"
Under the hood
View Binding will generate one binding object for every XML layout in your module.
For example for this activity_main.xml
layout file
View Binding will generate ActivityMainBinding.java
View binding will generate a correctly-typed property for each view that has a specified id
. It will also generate a property called rootView
.
View Binding is Kotlin friendly as all properties are annotated with @Nullable
or @NonNull
Kotlin knows how to expose them as null-safe types
In ActivityMainBinding.java
, view binding generates a public inflate
method.
It calls bind
where it will take the inflated layout and bind the properties, with some error checking.
The bind
method is where generated binding object will call findViewById
for each view to bind.
What about the Parcelize feature from Kotlin Android Extensions?
Don’t forget that Parcelize feature in Kotlin is part of the kotlin-android-extensions
compiler plugin, so removing the plugin will endup making all your Parcelable classes not compiling if they depend on the Parcelize
annotation.
JetBrains extracted the Parcelize from Kotlin Android Extensions to a new plugin, kotlin-parcelize
First you will need to add kotlin-parcelize
plugin to your module.
Then change your old import statement from
import kotlinx.android.parcel.Parcelize
to
import kotlinx.parcelize.Parcelize
Example
You could find this IDE error
Class 'User' is not abstract and does not implement abstract member public abstract fun describeContents(): Int defined in android.os.Parcelable
Don’t worry your app will build fine, this is a false positive error and was reported on youtrack and marked as fixed so should be shipped in the next release hopefully.
Please note this plugin is only available starting from version 1.4.20-M2
which is the same version that deprecated kotlin-android-extensions
compiler plugin.
If you want to give it a try you need to install the Kotlin EAP Plugin for IntelliJ IDEA or Android Studio
- Select Tools → Kotlin → Configure Kotlin Plugin Updates.
- In the Update channel list, select the Early Access Preview 1.4.x channel
- Click Check again.
- Then click Install
Tl;dr
Here is what you should do to migrate from kotlin-android-extensions
Kotlin plugin to ViewBinding
and kotlin-parcelize
plugin
- Remove
kotlin-android-extensions
plugin from yourbuild.gradle
files. - Remove all kotlin synthetic import statements from your activities and fragments.
- Enable
viewBinding
feature in your modulesbuild.gradle
files. - Add binding object in your activities and fragments and use it to set the content view and access views from your xml files.
- If you are using
Parcelize
annotation, then add the newkotlin-parcelize
plugin into your modulesbuild.gradle
files and change the import statements as mentioned above.
References
- YouTrack | Deprecate Kotlin Android Extensions compiler plugin
- YouTrack | Move the Parcelize functionality out of the Android Extensions plugin
- JetBrains/kotlin |Parcelize: Add integration test for the new kotlin-parcelize plugin
- Developer Advocate for Android at Google comment on a reddit thread
- Android Developers | Use view binding to replace findViewById