While looking at performance problems in my app, I discovered that each button press was triggering a call to the full onMeasure()/layout() cycle. There\'s no reason that I can
Does anybody have any experience with this? Is there any way to determine why a layout cycle was triggered?
There is a way to determine exactly why the layout cycle is triggered.
Go to the layout of the screen you want to debug and replace the topmost container with a custom container that overrides just one method: requestLayout().
So, for instance, if you layout looks like this:
You'll first need to create a new custom class that's a subclass of your container class:
public class RootView extends DrawerLayout {
public RootView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void requestLayout() {
super.requestLayout();
}
}
And then you'll need to modify your xml:
Now, the way android works is that when any viewgroup recieves layout request, it'll also call requestLayout() of it's direct parent. Which means that whenever any requestLayout() call is made inside your screen, your RootView's requestLayout() will also be called.
So start a debugging session, place a breakpoint inside the RootView.requestLayout() method and perform an action that you think is causing the layout cycle. Look at the stack trace. It'll always look like this:
RootView.requestLayout() line: 15
RelativeLayout(View).requestLayout() line: 17364
RelativeLayout.requestLayout() line: 360
FrameLayout(View).requestLayout() line: 17364
... a dozen of other calls to requestLayout() ...
TimeCell(View).requestLayout() line: 17364
TextViewPlus(View).requestLayout() line: 17364
TextViewPlus(TextView).setTypeface(Typeface) line: 2713
... more methods ...
The first method that is not requestLayout() is what is causing the layout cycle to happen.
In the example above, it is TextViewPlus.setTypeface(Typeface).
By resuming the program and waiting until the breakpoint triggers again you can quickly determine all the methods that trigger relayout in your particular case.
However, please note that interface lags are almost always caused by multiple measure calls, not by a multiple layout cycles!
In a complicated layout (scroll views, linear layouts with weights, etc.) single layout pass can require onMeasure to be called multiple times on a single view, sometimes up to 500 times per layout cycle and more. To check if this is your problem, override onMeasure and onLayout methods of one of the bottom views (one of the views that's further away from rootView; note that onLayout to onMeasure ratio will be different for different views on you screen). It might be hard to determine exactly which view has the worst onLayout to onMeasure ratio, but any views that's inside LinearLayout inside LinearLayout inside LinearLayout inside LinearLayout... is a good place to start.
If onMeasure is called more than 16 times per onLayout then you have a problem. Try making your view hierarchy more flat, remove LinearLayouts with weigthSum attribute and ScrollViews.