tir38
tir38

Reputation: 10431

How does Kotlin Android Extensions replacement for findViewById prevent null views?

I know that Kotlin's Android Extensions creates synthetic properties + caching function to replace any need to call findViewById:

All of these examples show that the similar java code would look like

private HashMap _$_findViewCache;
...
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;
}

public void _$_clearFindViewByIdCache() {
   if(this._$_findViewCache != null) {
      this._$_findViewCache.clear();
   }
}

What I don't understand is how this prevents potential NPEs? var2 = this.findViewById(var1); may still return null.

Using the example from that last link:

<TextView
        android:id="@+id/welcomeMessage"
        ... 
        android:text="Hello World!"/>

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    welcomeMessage.text = "Hello Kotlin!"
}

What type is welcomeMessage? TextView or TextView?

Upvotes: 1

Views: 1136

Answers (3)

Milan Jurkulak
Milan Jurkulak

Reputation: 635

I got crash with NPE also in listener. I made ZoomRecyclerView, which call onZoom listener method onZoom when zoomed. This event is then propagated to activity, where i call synthetic imagebuttonview method, to set image resource to set zoom button image if item is zoomed or not. It simply crash on synthetic call.

Example :

zoomRecycler.onZoom {
  // exception here : zoomButton must be not null
  zoomButton.setImageDrawable(...)
}

zoomButton.click {
  // calling zoom which raise onZoom inside zoomRecycler
  zoomRecycler.toggleZoom();
}

Upvotes: 0

musooff
musooff

Reputation: 6862

While @CommonsWare's answer is correct, I also wanted to spare my 2 cents on this subject.

As @CommonsWare pointed out, you have to import relevant layout in order to be able to use Kotlin Extensions. The tricky part here is, it's not only about importing relevant layout but also inflating the layout before you call it with Kotlin Extensions.

So, if you have something like below

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    welcomeMessage.text = "Hello Kotlin!"
    setContentView(R.layout.activity_main)

  }
}

You will still get

java.lang.IllegalStateException: welcomeMessage must not be null

And your app will crash.

Upvotes: 0

CommonsWare
CommonsWare

Reputation: 1007228

What I don't understand is how this prevents potential NPEs?

It doesn't. If you try referencing a widget that does not exist, you crash.

So long as your import statements are only for the relevant layout for your Kotlin code, you should not wind up referencing a widget that does not exist. Where the problem comes in is if you accidentally import the synthetic properties from another layout.

For example, suppose you have a project with activity_main.xml and scrap.xml layouts, and your activity is:

package com.commonsware.android.myapplication

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.scrap.*

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    scrapView.visibility = View.GONE
  }
}

Here, we are referencing a scrapView view in the scrap layout. We have not inflated that layout, and so this crashes with an IllegalStateException:

 Caused by: java.lang.IllegalStateException: scrapView must not be null
    at com.commonsware.android.myapplication.MainActivity.onCreate(MainActivity.kt:14)

What type is welcomeMessage? TextView or TextView?

Technically, it is TextView!, where ! means "it's a platform type, so we don't know if it can be null or not". Practically, TextView! is used the same as TextView, which is why you crash if it winds up being null.

Upvotes: 3

Related Questions