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?
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.
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