I know this seems to keep coming up, but I’ve yet to come across a really thorough objective comparison of all the available ways of assigning Android views from inflated layouts. I’ve got a team that I’m just now starting to bring up to speed with Kotlin and I’d like to present a preferred approach before we go too far down one path, rather than ending up with several different approaches for the same thing in the same codebase. I’ll try to list the options I’ve considered and the pros/cons I’ve found so far. We do not currently use Butterknife or any other approach other than findViewById()
in our Java code.
Most of the discussions I find seem to be mostly just personal preference and don’t really present much of a case for or against specific approaches, but there are certainly tradeoffs. Maybe the performance differences and object allocation differences between the approaches are so relatively minor that it really does come down to a matter of personal preference.
Nullable vars and findViewById()
private var textView: TextView? = null
…
textView = findViewById(R.id.text_view) as TextView
Pros:
- probably the most straightforward for a developer coming from Java
Cons: - view properties still need to be vars
- usages all need to deal with null
-
findViewById()
feels like it’s boilerplate - not very concise code - need to declare property in one place and initialize it elsewhere
lateinit
vars and findViewById()
private lateinit var textView: TextView
…
textView = findViewById(R.id.text_view) as TextView
Pros:
- still fairly understandable, no hidden code
- no need for usages to deal with null - they can all assume non-null
Cons: - view properties still need to be vars
- still needs
findViewById()
which feels like boilerplate - not very concise code - need to declare property in one place and initialize it elsewhere
Delegates.notNull()
and findViewById()
private var textView: TextView by Delegates.notNull()
…
textView = findViewById(R.id.text_view) as TextView
Pros:
- no need for usages to deal with null - they can all assume non-null
Cons: - view properties still need to be vars
- still needs findViewById()
- not very concise code - need to declare property in one place and initialize it elsewhere
- extra objects and indirection needed for the delegates
Lazy properties that use findViewById()
private val textView: TextView by lazy { findViewById(R.id.text_view) }
Pros:
- view properties can be val rather than var
- the
findViewById()
calls are not executed until the view is referenced
Cons: - thread safety overhead unless using
lazy(LazyThreadSafetyMode.NONE)
- unable to clear cached views in a Fragment’s
onDestroyView()
- extra objects and indirection needed for the delegates
Butterknife with lateinit
vars
@BindView(R2.id.text_view)
internal lateinit var textView: TextView
Pros:
- the generated code should be efficient
- has the ability to clear cached views in a Fragment’s
onDestroyView()
Cons: - view properties still need to be vars
- view properties can’t be private
- remembering to use
R2
rather thanR
in library modules is mildly frustrating
Kotterknife
private val textView: TextView by bindView(R.id.text_view)
Pros:
- view properties can be val rather than var
- view properties can be private
- has the ability to clear cached views in a Fragment’s
onDestroyView()
(if you apply one of the patches that adds this support) - avoids some of the thread safety overhead in comparison with lazy properties
Cons: - extra objects and indirection needed for the delegates
Kotlin Android Extensions
(no code sample, just get the correct imports added and start using the synthetic generated properties)
Pros:
- has the ability to clear cached views in a Fragment’s
onDestroyView()
, and even does so automatically - don’t need member properties (either val or var) for the views
- first-party (Kotlin/JetBrains) support
Cons: - seems a little too magic for some people (I can’t really figure out why)
Android data binding
binding = FragmentLayoutBinding.inflate(inflater, container, false)
...
binding.textView.text = "Hello"
Pros:
- don’t need member properties (either val or var) for the views
- first-party (Google) support
Cons: - adds additional dependencies to the project
- seems like maybe overkill if not using any other features of data binding