Is Android layout really exponentially hard? - android

Some commonly used android layouts such as
RelativeLayout and LinearLayout (when weights are nonzero)
have onMeasure() implementations that measure their children
twice, resulting in exponential running time when nested.
This is easily verifiable by emitting Log entries
from a leaf View's onMeasure()... it gets called 2depth
times.
Can someone give a clear and specific description as to why this is?
Most importantly, is this exponential behavior due to an important part
of the overall contract, or is it just an implementation detail
that might be optimized away? If it is believed to be unavoidable,
please give an example that requires it.
Such an example would greatly help me and others
who are grumbling that the "keep your layouts shallow"
mandate is burdensome and who are wondering
whether this is being driven simply by
not-yet-optimal algorithms in the core libraries,
or whether there really is a fundamental difficulty
blocking a fix.
Perhaps a minimal example would consist
of a Button inside a LinearLayout inside another LinearLayout
(with match_parent and weight=1 everywhere, to trigger
the full exponential behavior),
with some additional parameters or circumstances
making it clear that all four of the calls
to Button.onMeasure() are indeed meaningful and necessary.
My first guess would be that only two linear-time
traversals are really needed-- the first traversal to gather everyone's
preferred sizes, the second traversal to distribute slack
and/or shrinkage.
Other layout engines in the world, such as those for Tex and Swing and HTML,
seem to be able to routinely handle very deep hierarchies
having lots of alignment constraints and stretches,
without any exponential blowup, and I imagine that's how they work.
Please note, I don't want answers explaining how
the exponential blow-up occurs-- I understand that,
and there have been several posts already where that has been
asked and answered:
Why are nested weights bad for performance? Alternatives?
Android layout measuring time doubles with each step up the hierarchy
Layout Weight warning Nested weight bad performance
Efficiency of Android Layout hierarchy
http://android-developers.blogspot.com/2009/02/android-layout-tricks-1.html
My question is whether the recursive double-measuring is
fundamentally necessary/justified,
and if so, I'd like a clear explanation/example showing why.
Edit 2013/8/22: I think maybe my question is still not getting across.
I'll try to clarify and explain my motivation, more boldly this time.
Layout is not an exponentially hard problem,
as evidenced by efficient layout engines in the world, such as those for Tex and Swing and HTML.
So, what happened with LinearLayout,
and how should the android developer community proceed in response?
I am asking not in the spirit of laying blame,
but rather to understand and decide how to best move forward.
I can think of 4 possibilities:
Fix the performance bug in the core library, without changing any contracts
Change contracts as needed, and fix the performance bug in the
core library
Write an alternative to LinearLayout, that has its essential
features (namely distributing extra space among children in specified proportions) but without the performance bug, and use it for new apps
Continue to micromanage our layouts to work around the
performance bug for the rest of our android development careers.
(4) isn't a serious option for me personally.
Furthermore it seems clear to me that
changing the behavior of LinearLayout at this point is impractical,
so I don't believe (2) is a serious option either.
That leaves (1) and (3).
I'm capable and willing to do either of those personally, but which one?
Obviously (1) is far preferable if it's possible-- so, is it possible?
That seems to be the crucial blocking question that needs to be answered
in order to determine how to move forward.
I have spent some time in the core code
and the doc and it's not becoming clear,
so that is why I'm asking the question here.

In terms of measuring children twice, it's my understanding that this is what happens with LinearLayouts particularly when weights are involved. The best explanation I've found for this comes from RomainGuy in one of his presentations.
He has a slide about this and briefly speaks to it at 17:45. Feel free to rewind to get a bit of context though. You can find the video I'm referencing here: Devoxx'10 - Dive Into Android
Basically what he says is that on the first pass they calculate the total width or height depending on orientation of the LinearLayout, add the weights of the children, and find out how much space is left over, then on the second pass with that information they are able to properly divvy out all the remaining space to all the children. Simple enough.
I'd also like to point out though that yes, while it's very true that shallow layout hierarchies have less of a performance hit, if you are adding just 1 or 2 extra layers, you probably aren't going to see a big performance impact for the user. Once it's laid out, it's done. Even in ListView's if you properly use the given "convertView", and set up ViewHolder's, you're going to get good performance.
I'd encourage you to use DDMS and do a layout dump of some of Google's apps. They are very complex, and often times surprisingly deep, but they still get good performance. Don't be stupid with your layouts, but if it saves you time, adding an extra layout isn't the end of the world.

From here: http://developer.android.com/guide/topics/ui/how-android-draws.html
A parent View may call measure() more than once on its children. For example, the parent may measure each child once with unspecified dimensions to find out how big they want to be, then call measure() on them again with actual numbers if the sum of all the children's unconstrained sizes is too big or too small (that is, if the children don't agree among themselves as to how much space they each get, the parent will intervene and set the rules on the second pass).
It seems they view the measurement pass as a dialog between parent and children. In other words they opted for maximum flexibility instead of optimization. It still seems like they could optimize the base layouts though.

I came here tonight to ask this. It's disappointing that nobody else even seems to understand your question. After thinking about it for a couple more hours, I think I might know the answer.
Consider this example:
The red box represents a LinearLayout with vertical orientation. The green box represents another LinearLayout with horizontal orientation. I would expect the layout to proceed like this:
1) The red LinearLayout would measure the heights of green LinearLayout and the large blue widget on the bottom, then compare their weights, divide any leftover vertical space accordingly, and measure the children again. (The important thing to know here is that "measuring" a view actually sets its size.)
2) The green LinearLayout would then measure the widths of its three children, compare their weights, divide the horizontal space, and "measure" again.
The catch is that in order for the red layout to measure the height of the green layout, the green layout needs to know the height of its children.
Now, you would think that LinearLayout would be smart enough to optimize away many of its calculations. For example, there is no logical reason for the green layout to measure the width of its children when determining its own height. And if its children all have a height of fill_parent, then it shouldn't need to perform any calculations at all.
But the API doesn't allow LinearLayout to be that smart. The fundamental problem is that there is no way to measure only one dimension of a view. You can get them separately after they've been measured, but the actual measurement is done by View#onMeasure(int, int). The arguments to that method are encoded with View.MeasureSpec, and there's no way to encode "ignore this dimension." So the green LinearLayout stupidly calculates both dimensions of all its children when the red layout measures it, then repeats the entire process again when it does its own layout. The widths will not have changed the second time around, but they must still be recalculated because, again, there's no way to tell the layout or its children to measure only one dimension.
So to answer your question... no, it doesn't necessarily need to be this way, at least for many common use cases. This is a deficiency of the Android API.
If Google were to add new methods to View to measure dimensions separately, the default implementation would have to rely on onMeasure(int, int), which could be even worse for performance. But if there are any unused bits in the View.MeasureSpec encoding, then it might be possible to add an "ignore" flag that would allow future versions of LinearLayout to be better optimized.

The problem in my opinion is the measure cache. As far as I could see the cache only works if there was a layout of the view in the middle, so when you perform two consecutives measures of a child (even with the same exact measure specs) in the same "onMeasure" all the children and their sub-children are measured again. If the measure cache worked fine, the second measure should be much faster as it would take the cached values and it wouldn't cause all the children hierarchy to be measured again.

Related

How to measure the layout performance in android?

I had been using nested views in one of the screens in my android app. Now that I have removed all of them and used a ConstraintLayout for performance gains, I want to measure how much the performance has increased. Is there a way to measure the performance gain?
The easiest way is probably just to profile the app using a profiler using the old code and the new and see how much inclusive time is spent in onLayout in both versions.
Expect the difference to be less dramatic than you expect. The reality is your main thread is going to be idle most of the time, improving layout efficiency is really rarely a big factor in your app's performance. I would never refactor a nested design for that, I'd refactor a nested design if using constraint layout gave me cleaner XML for my view. Also remember that depending on what features you use, constraint view may not be faster. Its quicker to layout a nested linear layout than it is to calculate a whole bunch of constraints on multiple inner views, for example.

RelativeLayout vs nested Linear Layout performance

For a long time I heard that RelativeLayouts are slow. In a couple of talks I recall hearing that this type of layout calls onMeasure twice and that for some reason is a drag to performance.
However, I am taking a performance course at udacity and I watched the following video:
https://www.youtube.com/watch?time_continue=303&v=gK9tdeqqigE
Here, the instructor used the Hierarchy viewer tool to compare the rendering cost of the same viewgroup using a relative layout and nested linear layouts.
In the video, the relativelayout is the clear winner, which contradicts everything that I have heard until now about the issue.
Could you please help me to understand in which circumstances each approach is better?
Thank you
That's a very broad question and there's no single, simple answer. LinearLayout is generally simpler (and therefore faster) than RelativeLayout, but LinearLayout has a problematic case if you nest multiple of them inside each other, with weights on the same axis. Then it has to iteratively divide up the space and this takes lots of layout passes (it's so bad there's a lint warning against this).
Even when you avoid that case, then with nested LinearLayouts you will still have a deeper view hierarchy compared to using RelativeLayout, so while LinearLayout is faster, that balances out at some point.
So it becomes the same thing as with all things performance: the only way to be absolutely sure is to measure and see what happens.

Nesting Linear Layouts with Weights, Why Considered Bad?

So, I've been developing on Android since about Feb 2011. One thing I've always been acutely aware of is that Nesting Linear Layouts with weight and weight sums is 'bad for performance' and the compiler (well at least eclipse did) thew a warning fit when ever you dd so.
However, it's been my experience that doing this has no noticeable impact on the user experience, or speed when switching screens. As an experiment I created the following screen which deliberately features nothing other than a mega O.T.T. use of weights and nested linear layouts. I've used contrasting colors to show all the linear layouts used. The benefits is that it looks the same on large and small screens.
I've tried it on a Galaxy S4, Galaxy Note 10.0, Galaxy note 3 7", and very small low powered Galaxy Neo. I've tried it on Gingerbread, Cream Sandwich, Kitkat and Lollupop and do not see any perceptible speed difference between this screen an a simple one with 4 buttons on a relative layout.
So what makes this such a bad idea? What is going on under the hood that makes this a less than idea solution? Will all this change the moment I add drawables or images into the mix?
The main issue here has to do with how Android positions your views on screen. Any time that a position changes with respect to a view, then a "layout" pass cascade through the view hierarchy, touching each view and performing some calculations (similar for changing "sizes" kicks off a "measure" pass).
Some layout containers, like RelativeLayout, have to force a 2nd "layout" pass to kick off; Once all the views have calculated their desired position, ReltativeLayout needs to then re-position everything again based on it's layout properties.
This causes a Double Layout Taxation trough the view hierarchy; which isn't free.
Now, if you have a shallow hierarchy, the overhead of this action is insignificant. Where this starts to run into a problem is when you've got a deep hierarchy, with multiple double-layout containers in it. For example, having a RelativeLayout at the root, which contains LinearLayouts that have "measure with largest child" set on it. The result is that the leaf-node views will have "layout" called on them ~8x times.
The performance impact of this is directly correlated to how complex your layout/measure passes are in your scene. If nothing is problematic, maybe a 12x-per-view layout pass isn't a big deal on high-end devices. However if one of the leaf nodes has a complex layout pass, well, then you're just wasting cycles.
The main topic here is to profile for things that are a problem, but keep in mind that setting things up the wrong way is just wasting performance that could be used elsewhere.
So what makes this such a bad idea?
It's not a bad idea. It is not free, either.
In particular, your scenario is fairly simple, despite your protestations to the contrary. While your layout is rather complex, it is rendered once. Hence, while you may drop a few frames while rendering it, that price is paid one time (or, more accurately, one time per activity/fragment that is using this layout).
Where per-layout expense becomes a much bigger problem is when it is magnified by having several of them that get animated around, such as rows in a ListView/RecyclerView, or pages in a ViewPager. Now, we are going through lots and lots of rendering passes, as the user swipes and causes us to redraw our content. Each individual row in the list might be significantly simpler than your layout, but we're also going to be trying to draw those many times per second. And, while a couple of dropped frames may not be noticeable in your case, dropped frames during animation is pretty much the definition of "jank" for an Android UI.
So long as you have no jank -- IOW, you are not dropping any frames, as reported by Choreographer in LogCat or as seen in the gfxinfo overlay through Developer Options -- then your layout is fine, at least as far as I and probably most users are concerned. Conversely, if you are dropping frames, trying to figure out more efficient layouts, perhaps even custom ViewGroups rather than general-purpose ones, may help alleviate that jank.

onMeasure is called 1,500+ times

I've recently noticed a significant performance issue with my app. I essentially have the following layout...
DESCRIPTION
In short, there is a ViewPager that holds 3 RelativeLayouts as pages. Inside each page there a number of TextViews. I recently noticed a lot of lag when I type into the EditText that has a TextWatcher on it to perform a quick SQL query for autocompleting. My HTC One M8 stutters and lags as I input text, but I know it isn't the querying that is slow because I measured queries taking only about 7 ms.
I used method profiling, Systrace, and plain old Log debugging and concluded that each time I typed a character in the EditText about 1,700 calls to onMeasure were being made on the TextViews within RelativeLayout A, B, and C. Cumulatively, there are only about 15 independent TextViews across the pages. I noticed that each onMeasure is being called hundreds of times instead of just once or twice like it usually might.
QUESTION
I don't know why typing in the EditText in page B would cause other TextViews in pages A and C to also get "re-measured". More importantly, does anyone have insights on why onMeasure is being called so often? And, does anyone know of a solution to significantly reduce the number of calls to onMeasure?
DETAILS
This may help: I was actually able to cut the the number of onMeasure calls to about 900, by removing RelativeLayout 2 which could suggest the propagation of onMeasure calls starts outside of the ViewPager.
While I don't have enough information to identify the specific problem, I can tell you, in general, what is happening:
A call to a root view's measure causes a call to each sub-view's measure and so on, down the tree.
Suppose that the view called "current view", after asking each of its sub views to measure themselves, decides that there is not enough space for all of the children. It will ask them each to measure themselves again, suggesting a max size. Suppose, then, that the view called "Relative Layout B" changes its size, radically, based on the specified max. Because the change is so big, "current view" must asks each child one more time. This loop could easily require a couple more iterations to resolve.
When "current view" decides on its size, it reports back to "View Pager". Suppose that "View Pager" does not have enough space for its three views, and has to ask them to re-measure, with a max. We are now up to at least 20 calls to the TextView's onMeasure. If each of the other views above ViewPager iterates a few times, pretty soon you start seeing big numbers.
The best solution, as you've already pointed out, is to reduce the depth of that tree. Alternatively, you might use layout managers that have much simpler measurement algorithms (FrameLayout, LinearLayout)
1500 is a lot though. One of those views is doing something weird. You should be able to identify it very simply, using the TreeView tool in the DDMS suite. In eclipse it is called "TreeView". In Studio, click the Android icon and switch to the Hierarchy View perspective. You can also run it from the command line, from the SDK tools directory.
Added:
If you connect Hierarchy Viewer to your app while it is running, you will see the entire view tree in one of its panes. Each node in the tree will have three colored gumdrops towards the bottom. The first gumdrop represents the measurement phase. If the gumdrop is green, the measurement phase for that view is fast. If it is yellow, that node is in the top 50 percentile of nodes in the layout. If it is red, it is the slowest in the layout.
You should be able to follow a trail of yellow right to the offending view.

Two-pass UI Layout : Why?

I've noticed that Android, WPF, and Silverlight all follow a two-pass layout pattern. There's a recursive Measure() method that gets called to size the element, possibly multiple times. Then a recursive Layout/Arrange() method is called which lays out the exact positions of children in their parent control, and will also set the final size of the control.
My question: why is this split into two passes, especially when, for some types of controls, Measure() can't compute the actual size of a control without actually laying out the positions of the children? Is there some type of layout minority case that is made possible by this?
I'm trying to create my own UI toolkit, and I'm currently leaning toward a one-pass Layout() pattern, but I'd like to be convinced whether this is wise or not.
Thanks for reading this :)
Sean
The reason for the two passes is that any element in the structure can influence the remaining available space of the others.
Some element wish to take the largest possible space while others have fixed dimensions. You can also have elements with only a max width set. It creates an equation that can not be solved in one pass.
The different panels in the hierarchy ask the elements what size they need in the first pass, then distribute the space among them according to each panel's nature, and finally informs each element of its allocated space.
EDIT: Some more explanations
The main drawback of a single pass layout is that you are handling each element sequentially. A first element takes a certain amount of space and the others take the rest. Why is this element the first? Try your algorithm with different element order, and you will have different resulting layouts.
A two pass layout simulates a parallel behavior where each element influences the whole layout.

Categories

Resources