23年8月22日15:20勘误:文章说到View完成了Lifecycle接口是错的,正确的状况是将一个LifecycleoOwner通过View的Tag的办法绑在了它身上。然后可以通过该View的getTag()访问到LifecycleOwner。 原文已批改。

故事布景:

小明是一个25岁刚入行不久的安卓初级摸鱼工程师,他仍然记住第一次运用Compose的时分的振奋与严重,自从有机会在公司项目布置Compose之后,小明就开端将公司的基于xml完成的组件过渡到Compose,可是有一天在修改了某个Dialog的的内部View之后,小明惊讶的发现Dialog弹出时直接溃散了,这让小明深入了深深的自我置疑:Compose不是可以和安卓原生混合开发吗,为什么连Dialog这种根底场景都出问题了?

所以这一切产生了什么,是人道的歪曲还是品德的沦丧?欢迎收看今天的《走进Compose》,为你解密底细。

1.代码复盘

让我们看看小明的Dialog代码:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

小明挑选直接承继了Dialog,然后在setContentView中参与一个ComposeView,运转的时分就直接溃散了,让我们再看看溃散:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

中心溃散是在ComposeView找不到ViewTreeLifecycleOwner,而实行这段代码是在WindowRecomposerFactory下面的一个伴生方针LifecycleAware在实行createRecomposer()中。

由于触及到了Compose底层源码,本文中不会细究源码而是只聚焦溃散产生的原因。

2.中心溃散代码定位

刚才说到了,问题代码是在WindowRecomposerFactory下面的一个伴生方针LifecycleAware在实行createRecomposer()中。

所以我们直接跳到WindowRecomposerFactory

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

这是一个工厂类,作用是构建Recomposer,并且是Android Window ScopedRecomposer

Recomposer是用于实行重组的中心类,可是这不是这篇文章的要点,要点是Android Window Scoped,这是什么概念呢,其实指的就是这个Decomposer是会感知ComposeView地址的顶级父ViewGroup所持有的Lifecycle。

或许你看到这儿会有点晕,我略微做个总结:Recomposer是实行重组的中心类,而它会通过ComposeView来获取到LifecycleOwner来感知的生命周期。

那么问题来了:为什么要监听生命周期?能通过View获取到Lifecycleowner然后感知生命周期吗?

答:

  1. 假设其时的ComposeView处于不可见的状态,则不需求糟蹋手机性能去制作帧或者制作动画,因此需求感知生命周期。
  2. View在安卓原生代码中是没有生命周期一说的,其实安卓原生代码都没有生命周期这个概念,留心这儿指的生命周期是特指Lifecycle这个库,这个库是Jetpack库额外供应的才干。

当问题分析到这儿,底细现已呼之欲出了:构建Recomposer的时分,它会去通过其时ComposeView来找到LifecycleOwner,假设找不到,就报错了,所以出现了开头的一幕:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

看到这儿你或许会更疑问了,ViewTreeLifecycleOwner是什么东西?为什么之前运用ComposeView的时分,从来没有人为设置过,ComposeView也能找到ViewTreeLifecycleOwner呢?

3.直击ViewTreeLifecycleOwner

我们从这儿进去看看是怎么根据ComposeView创立一个生命周期感知的Recomposer

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

补:WindowRecomposerFactory里边那个rootView就是ComposeView

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

中心代码现已出现,Recomposer是怎么找到ComposeView的生命周期的?答案就是findViewTreeLifecycleOwner(),这是View的一个扩展办法,它是由Jetpack里边的Lifecycle库供应的,直接跳转进去。

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

这个办法或许不熟悉kotlin的Sequence的童鞋会看不太懂,这个就是一个递归查找父级,看一看假设父级是否带有R.id.view_tree_lifecycle_owner这个Tag,一起它是LifecycleOwner,取效果第一个。

这儿画一张简易的图来表示这个代码的含义:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

`ComposeView`往上查找最顶级那个符合条件的`View`,然后通过`View`的Tag找出`LifecycleOwner`,以此作为`Recomposer`来辨认其时窗口生命周期的根据。

所以,回归开端的问题:为什么Dialog中运用ComposeView直接溃散了?

:由于DialogWindow中不存在一个符合带有R.id.view_tree_lifecycle_owner的Tag并且该Tag的实例为LifecycleOwner这个接口的View。

问题又又来了:为什么之前在Activity、Fragment、DialogFragment中不存在溃散的问题?

:由于这些组件的Window中存在符合条件的View

问题又又又来了:为什么存在?似乎自己没有设置过。

:Jetpack库的Activity、Fragment等组件都默认完成了。

仔细的读者现已发现,ViewTreeLifecycleOwner有一个设置的办法:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

通过检查调用的当地,总算破案:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

在`ComponentActivity`的专门为Compose供应的扩展办法中现已为`DecordView`提前设置了`ViewTreeLifecycleOwner`,这是`ComposeView`能在Activity中感知生命周期的原因。

至于Fragment,设置则是这儿:

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

留心:这儿设置viewTreeLifecycleOwner的方针并不是Window的DecordView,而是Fragment的onCreateView()创立的

小明这时分总算想起来了,之前用的Activity并不是按照源码的基类,而是ComponentActivityFragment也是AndroidX库里边的,这些组件都是赋值了viewTreeLifecycleOwner,因此ComposeView可以辨认他们的生命周期并做一些不可见的时分的优化,并在合适的时分开释资源。

而安卓原生的Dialog是没有做以上工作的,这就是直接在Dialog中运用ComposeView会导致溃散的原因。

从头检查setViewTreeLifecycleOwner()调用的当地,小明惊喜的发现了ComponentDialog这个类运用了这个办法,而这个类是Dialog的直接子类。

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

所以小明振奋的把Dialog改成ComponentDialog,从头运转代码,完美解决问题!

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

Dialog里边用ComposeView竟会直接闪退?深挖Lifecycle与Compose的爱恨情仇

4.总结

本案的中心问题是ComposeView要测验在它地址的View树中找到Lifecycle,而这个Lifecycle是Jetpack库为ActivityFragment额外供应的才干,因此我们可以在Jetpack库的AndroidFragmentDialogFragment直接运用这个才干,而安卓原生的Dialog是没有这个才干的,因此在Dialog中直接运用ComposeView会导致溃散。

为什么谷歌要搞一个viewTreeLifecycleOwner的办法呢,其实就是早期的安卓源码设计中缺少一种优良的生命周期监听的办法,这种才干只能靠Jetpack库来额外供应了,因此我们在现代化安卓编程中尽量不要直接只用安卓原生的组件,而是运用Jetpack库中供应的,

假设这个文章解决了你的问题,请给笔者点一个赞,谢谢你的支持