avatarElye - A One Eye Developer

Summary

The article discusses the evolution of methods for accessing views in Android development, from the traditional findViewById with casting to the modern Jetpack Compose.

Abstract

The article provides a historical overview of the various methods Android developers have used to access and manipulate UI views. It begins with the original approach of using findViewById with explicit casting, which was prone to runtime errors due to potential type mismatches and null references. Over time, third-party libraries like ButterKnife emerged to reduce boilerplate code, although they still relied on the underlying findViewById method. Google then introduced Data Binding, which allowed developers to bind UI components directly in XML layouts, reducing null and type issues. Subsequent improvements included the introduction of generic findViewById to eliminate casting and Kotlin Android Extensions for a more streamlined approach with Kotlin. However, these methods have been largely superseded by View Binding, which offers compile-time safety and less boilerplate. Looking to the future, Jetpack Compose represents a paradigm shift in Android UI development by enabling reactive UI programming purely in Kotlin, eliminating the need for XML layouts and providing a more robust and concise way to build UIs.

Opinions

  • The traditional findViewById with casting is considered outdated and error-prone due to the potential for runtime crashes.
  • ButterKnife and similar third-party libraries were well-received for reducing boilerplate code, but they did not fully address the underlying issues of findViewById.
  • Data Binding was a significant step forward, moving modals into XML and providing a more secure way to access views, although its adoption was hindered by the need for additional boilerplate code.
  • The introduction of generic findViewById was seen as a welcome improvement, making code cleaner by removing the need for explicit casting.
  • Kotlin Android Extensions were praised for their simplicity and for leveraging Kotlin's capabilities, but they still faced potential null-pointer exceptions.
  • View Binding is currently the preferred method, offering a balance between safety, conciseness, and ease of use.
  • Jetpack Compose is anticipated to be the future of Android UI development, with its declarative approach and reactive programming model, which is expected to simplify UI logic and reduce common runtime errors associated with view access.

Learning Android Development

How Android Access View Item: The Past to the Future

The evolution Android Development on accessing view

Photo by Fatih Kılıç on Unsplash

Do you know, how old your code is, can be deduced by looking at how it accesses the view? Over the decade, the way Android code access the view has changed.

In the beginning (2008–2017): findViewById with casting

In Android development since it starts, the View has always been a separate XML file from the actual code.

For the Java code to

TextView textView = 
     (TextView) view.findViewById(R.id.textViewTitle);
textView.text = "Something

The developer has to tell the Java code the ID and the type (i.e. TextView) by casting it.

There is no guarantee you will find the view with the ID nor it is a TextView during compile time. It could crash during runtime.

3rd part AutoGenerate Binding (2014–2019): ButterKnife

Jake Wharton who is allergic to boilerplate code, came up with ButterKnife. This library since then became the defacto of how Android developers access View Item.

lass ExampleActivity extends Activity {
  @BindView(R.id.textViewTitle) TextView textView;
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
    textView.text = "Something"
  }
}

The nice bit about ButterKnife is, it is using Annotation to auto-generate code behind, so all the views can be injected into the class after we use ButterKnife.bind(this).

No more typing findViewById, or casting it to the type we need.

Still then, underlying it is using the original findViewById, and hence the issue of wrong type and null crash still possible during runtime.

Additional nice to know facts, there are cases one need to include a non-final view ID (e.g. from another module library), ButterFork library was introduce for that.

Move modal into XML (2015 — current): Data Binding

Accessing the View Item from the code seems to cause a lot of problem like no guarantee of Type conformance and null issue, Google introduce Data Binding.

Conventional View Update, where modal is in the code
Data Binding Update where modal is in the XML instead

From here you’ll see that the shift is to move the Modal Variable into the XML.

<?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android">
 
     <data>
         <variable
             name="myTextModal"
             type="String" />
     </data>
 
     <TextView
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:text="@{myTextModal}" />
 
 </layout>

In order to add the modal into the XML, an additional layer of layout is required to wrap around the data and the root view.

On the code side, one just need to access the modal directly

private lateinit var binding: ActivityMainBindingoverride 
fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(
          this, R.layout.activity_main)

    binding.myTextModal = getTextFromLogic()
}

So with that, no more type or null issue. It is more secured.

However, due to it’s boilerplate needed, it is still not very much adopted.

No more casting (2017–current): Generic findViewById

After 9 years of having

@Nullable    
public final View findViewById(@IdRes int id) {
    if (id < 0) {
        return null;
    }
    return findViewTraversal(id);
}

In 2017, SDK API 26, Google finally use generic to make casting not needed.

@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
    if (id == NO_ID) {
        return null;
    }
    return findViewTraversal(id);
}

Reference of source from https://github.com/AndroidSDKSources

With that, one can call findViewById without need to cast.

TextView textView = view.findViewById(R.id.textViewTitle);
textView.text = "Something"

Nicer code but still lose out to ButterKnife.

Kotlin Android Extension (2017–2020): Kotlin Synthetic

Kotlin was made the first-class citizen of Android Development in 2017 by Google. That’s the time Kotlin Android Extension (not to mix up with KTX) was also introduced.

To use it, just need to add the plugin, and that’s it.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

In your code,

  • no binding needed like what ButterKnife needed.
  • no findViewById, and no casting needed

You just need to type

text_view.text = "Something"

And the below will automatically imported!

import kotlinx.android.synthetic.main.view_layout.*

Underlying, the Kotlin Android Extension plugin will convert the code automatically in JVM to find the view. A little explanation here.

The automatic code conversion does solve the type issue. However, this doesn’t really fully solve the null-pointer issue. One can still accidentally provide a wrong ID (same View ID name but in a different layout) and causes the crash.

With View Binding coming up, the Kotlin Synthetics approach is deprecating.

The today preferred method (2019 — current): View Binding

In view of the imperfection of various approaches above, Google continues its quest to introduce a better way. View Binding is introduced.

In this approach, one bind a layout by accessing the generated layout class e.g.

private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.textView.text = "Something"
}
  • No more need to cast or define the type anymore.
  • Everything is given and ensure correct during compile time.
  • No extra boilerplate code in layout.xml as well.

Compare to Kotlin Synthetics, a little boilerplate of getting the binding. Other than that, one can safely access the View Item.

Hence with this introduction, both Kotlin Synthetics and ButterKnife consider themselves deprecated.

The future approach (2021 and beyond): Jetpack Compose

Of recent, Jetpack Compose, is in the horizon. It is based on react framework of UI update, and eliminate the need of XML.

The UI is made from Composable Function, which is in Kotlin code. There is no need of separate layer of XML to define the UI.

Even with this approach, the activity code doesn’t interact with the Composable Function code directly. Instead it goes through and intermediate state variable. The change happens to the State Variable will automatically recompose the view (reactive nature).

e.g. in the code

val textState = mutableStateOf("")
fun changeText(value: String) {
    textState.value = value // changing the state
}

As shown in the code above, just changing the textState.value, the below Composable function will recompose (redraw) itself.

@Composable
fun MyComposableText(textState: MutableStateOf<String>) {
    Text(text = textState.value) // will recompose when value change
}

To understand the recomposable function work, you can refer to the below blog

With the above, there are many benefits

  • No more concern of need to find ID
  • No more issue of getting null crash due to wrong ID
  • No more type casting or wrong casting crash
  • UI Logic can reside within composable function
  • Logic code doesn’t interact directly with UI Logic code, but go through state variable.

I strongly belive Jetpack Compose will be the future of how Android App interact with the UI.

For better illustration the conventional XML view with Jetpack Compose, check the below article out.

In summary, the below is a diagram of timeline.

There’s another great article on how the findViewById evolved by Wajahat Karim on. It provide a great table of how they differ from one method with another one.

If you like this article, the below might suite you as well.

Android App Development
AndroidDev
Android
Mobile App Development
App Development
Recommended from ReadMedium