Android layout measuring time doubles with each step up the hierarchy

こ雲淡風輕ζ 提交于 2019-12-08 04:30:18

问题


I'm analyzing a tablet UI in hierarchyviewer and I noticed the following pattern in measure times (starting from the base of the tree and moving up):

... ~40 ms ... ~80 ms ... ~160 ms ... ~320 ms ... ~640 ms ... ~1280 ms ...

I assumed the problem was LinearLayouts with nested weights, so I removed ALL LinearLayouts and weights in the whole hierarchy. Now I have this:

... ~40 ms ... ~80 ms ... ~160 ms ... ~160 ms ... ~160 ms ... ~310 ms ...

Better, but it still doubles every few levels. What could be causing this?

Here is the full hierarchy for this path (pardon the length... feel free to throw your best optimization tips at me):

[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]

*view time doubles

Any advice will be greatly appreciated! Thanks in advance.

TL;DR

Other than nested weights, what causes exponential increase in measure times?


回答1:


Previous answer from Simon isn't quite right. True there's a measure pass and and a layout pass, but that fact alone doesn't cause exponential blowup.

If you put traces inside onMeasure() and onLayout() on the various Views in the hierarchy, you'll find that the layout pass is not a problem: onLayout() only gets called once on each View, making it a linear-time traversal. The measure pass is the problem-- for some subclasses of ViewGroup, with some parameters, onMeasure() ends up calling each child's onMeasure() twice, which of course leads to exactly the behavior you're seeing.

RelativeLayout is one of these "bad citizens" that measures each child twice, agreeing with your observation.

Another bad citizen (as you know) is LinearLayout when children have MATCH_PARENT and nonzero weights. I've looked at the implementation and I roughly follow what it's doing-- first it recursively measures the children once to see how big they'd like to be, then, if there is any slack (or shrinkage), it measures the children with nonzero weights again in order to distribute the slack.

Note that RelativeLayout is often cited as a solution to the exponential blowup, even though it is one of the bad citizens causing it. I believe the reasoning is that RelativeLayout is expressive enough that deep hierarchies of LinearLayouts can be re-expressed as a single RelativeLayout. This is a very important point-- if you simply replace LinearLayouts with RelativeLayouts without flattening the hierarchy, you'll still have exponential measure times.

It's unclear to me whether the exponential blowup is something that can be eventually be simply optimized away in the core libraries (perhaps by separating the existing measure pass into a gather-preferred-sizes pass and a distribute-slack pass, each of which can be done in linear time and which don't need to call each other) or whether there is something in LinearLayout's and RelativeLayout's contracts that make the recursive double-measuring-of-children inherently necessary.




回答2:


Android builds layout in 2 passes.

The first is the measure pass, when all children are asked how large they want to be. Then a layout pass, when the parent tells the children how big they are and requests them to draw.

Therefore, adding an extra ViewGroup level roughly doubles the time. This is consistent with the examples you've given.



来源:https://stackoverflow.com/questions/13850509/android-layout-measuring-time-doubles-with-each-step-up-the-hierarchy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!