Jibbo
Jibbo

Reputation: 442

Why this strange behaviour of the `by lazy` delegate in fragments

A few days ago, I posted this question about using synthetic properties when you include the same layout in a screen multiple times.

The answer was awesome but after I tried it more for a few days, I noticed a strange behaviour:

When going forward from the fragment (the one containing the references to the view obtained by the lazy delegate) and then coming back ( I use transaction.commit() and manager.popBackStack(), to do this ), the labels will be empty. I already checked with the debugger if anything is null there, and nothing is.

The only solution that seems to work is replacing the by lazy with lateinit var and assigning them in onViewCreated.

Do you know why? Is the solution I used still "good" as a kotlin idiom?

I include the two pieces of code for the sake of completeness:

Partially working one:

private val foundTitle by lazy { detailContainer1.title }
private val foundDescription by lazy { detailContainer1.description }

private val wantedTitle by lazy { detailContainer2.title }
private val wantedDescription by lazy { detailContainer2.description }

Always working one:

 private lateinit var foundTitle: TextView
 private lateinit var foundDescription: TextView

 private lateinit var wantedTitle: TextView
 private lateinit var wantedDescription: TextView

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

   foundTitle = detailContainer1.title
   foundDescription = detailContainer1.description

   wantedTitle = detailContainer2.title
   wantedDescription = detailContainer2.description

 }

Thanks in advance

Upvotes: 5

Views: 2119

Answers (1)

Pawel
Pawel

Reputation: 17268

Fragment's have their view destroyed when they get removed - but lazy fields do not clear their reference so they are essentially leaking previous view.

If possible You should always have unique view IDs within your project, even if they are not within same layout - having duplicates can cause multiple problems (like yours).

If you were able to use kotlin extensions directly, it would generate code for finding, caching and clearing the view cache when fragments view is destroyed automatically.

Try to "get" views from fragments cache instead of assigning them to fields:

private val foundTitle 
    get() = detailContainer1.title
private val foundDescription
    get() = detailContainer1.description

private val wantedTitle
    get() = detailContainer2.title
private val wantedDescription
    get() = detailContainer2.description

Upvotes: 7

Related Questions