Android 布局测量时间随着层次结构的每一步增加而增加一倍

bma*_*mat 2 android android-layout hierarchyviewer

我正在 hierarchyviewer 中分析平板电脑 UI,我注意到测量时间中存在以下模式(从树的底部开始向上移动):

... ~40 毫秒 ... ~80 毫秒 ... ~160 毫秒 ... ~320 毫秒 ... ~640 毫秒 ... ~1280 毫秒 ...

我认为问题是带有嵌套权重的 LinearLayouts,因此我删除了整个层次结构中的所有 LinearLayouts 和权重。现在我有这个:

... ~40 毫秒 ... ~80 毫秒 ... ~160 毫秒 ... ~160 毫秒 ... ~160 毫秒 ... ~310 毫秒 ...

更好,但每隔几个级别它仍然会翻倍。可能是什么原因造成的?

这是此路径的完整层次结构(请原谅长度......请随时向我提出您最好的优化技巧):

[generated layouts]
 *RelativeLayout [309 ms]
   FrameLayout [164 ms]
    NoSaveStateFrameLayout [160 ms]
    *RelativeLayout [151 ms]
     *RelativeLayout [77 ms]
       ListView [45 ms]
        GridLayout [46 ms]
         Spinner [4.4 ms]
          TextView [0.1 ms]
Run Code Online (Sandbox Code Playgroud)

*观看时间加倍

任何建议将不胜感激!提前致谢。

长话短说

除了嵌套权重之外,还有什么原因导致测量时间呈指数级增长?

Don*_*tch 5

西蒙之前的回答不太正确。确实有一个测量通道和一个布局通道,但仅此事实并不会导致指数爆炸。

如果您将跟踪放在层次结构中各个 View 的 onMeasure() 和 onLayout() 内,您会发现布局传递不是问题:每个 View 上的 onLayout() 仅被调用一次,使其成为线性时间遍历。测量过程是问题所在——对于ViewGroup 的某些子类,使用一些参数, onMeasure() 最终会调用每个子类的 onMeasure() 两次,这当然会导致您所看到的行为。

relativeLayout 是这些“坏公民”之一,它对每个孩子进行两次测量,与您的观察结果一致。

当子级具有 MATCH_PARENT 和非零权重时,另一个坏公民(如您所知)是 LinearLayout。我查看了实现,大致了解了它在做什么——首先,它递归地测量子级一次,看看它们想要多大,然后,如果有任何松弛(或收缩),它就会测量子级再次使用非零权重以分配松弛。

请注意,RelativeLayout 通常被认为是指数爆炸的解决方案,尽管它是导致指数爆炸的坏公民之一。我相信原因是RelativeLayout 具有足够的表现力,LinearLayout 的深层层次结构可以重新表示为单个RelativeLayout。这是非常重要的一点——如果您只是将 LinearLayouts 替换为RelativeLayouts 而不展平层次结构,您仍然会有指数测量时间。

我不清楚指数爆炸是否最终可以在核心库中简单地优化掉(也许通过将现有的测量传递分为聚集首选大小传递和分布松弛传递,每个传递都可以是线性时间完成并且不需要互相调用)或者 LinearLayout 和relativelayout 的契约中是否有某些东西使得递归双子测量本质上是必要的。