Android Studio debugger causes early initialization of lazy vals


#1

Hi,

I have been working on a small sample project to evaluate Kotlin and to try out the language features. I have run into an annoying issue with lazy initialization.

According to the documentation here (https://kotlinlang.org/docs/reference/delegated-properties.html), lazy initializers should run on first use. This generally appears to be the case, but it appears that in Android Studio the debugger is causing lazy initializers to run when it attempts to show the value of a lazily initialized value.

Here is a sample project I made to test this consisting of a single Kotlin file and a single layout XML:

MainActivity.kt

package com.example.test.lateinitproblem

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView

class MainActivity : AppCompatActivity()
{
    val textView: TextView by lazy {
        Log.d("+++", "lazy init called");
        findViewById<TextView>(R.id.textView)
    }

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

        Log.d("+++", "About to reference lazy val");
        textView.setText("Hello World!");
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.test.lateinitproblem.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="XML Text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

All this app does is load an XML and attempt to change the text of a text view from “XML Text” to “Hello World!”. When I run it with no breakpoints set, it works fine and the log messages are printed in the order I would expect:
09-08 10:59:31.585 23564-23564/com.example.test.lateinitproblem D/+++: About to reference lazy val
09-08 10:59:31.585 23564-23564/com.example.test.lateinitproblem D/+++: lazy init called

However, if I set a breakpoint on the following line:

setContentView(R.layout.activity_main)

And run the app again, it crashes after I continue from the breakpoint. The order of the log statements also changes:
09-08 11:05:09.656 1485-1485/com.example.test.lateinitproblem D/+++: lazy init called
09-08 11:05:17.514 1485-1485/com.example.test.lateinitproblem D/+++: About to reference lazy val

I assume this is happening because when the breakpoint is hit, the debugger is attempting to show the value of the member variables of MainActivity, which is causing the lazy initializer to run before it should.

While this is just a sample project created to illustrate the issue, this is causing me issues in my larger test project. Ideally the debugger should not cause currently uninitialized lazily initialized fields to initialize earlier than they would otherwise.

I am also aware that there are other mechanisms that can be used to sidestep this issue, but I would like to be able to use lazy initializers without worrying about the order they are called being altered by using a debugger.

Thanks

Edit:

I forgot to post the source of the crash, if it wasn’t obvious. Since the lazy initializer runs before setContentView is called when the breakpoint is used, textView ends up being initialized to a null pointer. This results in the following crash which only happens when the debugger is used and a breakpoint is set prior to setContentView completing:


#2

Does anyone have any input on this? It still happens for me in the official release of Android Studio 3.0.

Can anyone else see if they can reproduce this problem with the given sample code?


#3

I doubt this will be ever fixed