The Interesting case of ViewTree*Owner
So, while building a sample app, I was famished by a very sketchy exception which is here down below
I was bamboozled when I saw this error, as this was happening only, when I was inflating a ComposeView
inside a fragment, if instead I inflate a layout using LayoutInflater
return it on onCreateView()
the code was running as expected. I looked around on StackOverflow, issuetracker and few blogs, and they all suggested to update the appcompat
to 1.3.0
or later, but I was already on 1.3.1
so, needed to figure out what went wrong where?
The Origin
So I started with a simple MainActivity
and a project with viewBinding
enabled, here is the snippet
So In here you can see, that I am using SampleBinding.inflate()
and attachToRoot
set to true, that basically attaches this layout to the window root, and shows it to the user, and I began my fragmentTransaction
which tries to inflate a DemoFragment
which in turn inflates a ComposeView
like below
and when I ran it, it crashed with the above stack trace.
After looking into the
ComposeView
andAbstractComposeView
, I came across a few code snippets which made me realize the obvious mistake that I was committing.
The Dig
So basically to lay out Composables
we need a Recomposer
which is just a Scheduler “for performing recomposition and applying updates to one or more Composition” this Recomposer
which needs to know to what lifecycle this needs to be binded to, this is resolved via a few way which can be seen in the below snippet from ComposeView.android.kt
this is all to create a LifecycleAwareViewTreeRecomposer
for obvious reasons. So at this point I knew that I messed up somewhere, and needed to know where, so I tried calling
ViewTreeLifecycleOwner.set(requireActivity().window.decorView, this)
before returning the ComposeView
in the DemoFragment
and it worked as expected.
but now to the main point, this can’t be the solution as I am going to do this manually for each and every fragment and that’s not the best solution.
The Solve
So, basically the whole concept of ViewTree*Owner was introduced recently, and there are three ViewTreeLifecycleOwner
, ViewTreeViewModelStoreOwner
and ViewTreeSavedStateRegistryOwner
and there name are self explanatory about what they are owner of, and in the latest source of appcompat
to be specific AppCompatActivity
you see that we have setContnetView()
method which inturn calls initViewTreeOwners()
and that does the following.
it sets all of them. So if you go back to MainActivity
you can see that I never called in the setContentView()
with binding.root
as parameter to it, thus skipping the call to initViewTreeOwners()
thus the exception.
Few links to reference the AppCompatActivity
, ComponentActivity
and all are below.