Slide 1

Slide 1 text

LAYOUT TRAVERSALS Devfest London, 2014

Slide 2

Slide 2 text

LUCAS ROCHA +LucasRocha | @lucasratmundo

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

BE DELIBERATE Less handwaviness in your UI code

Slide 5

Slide 5 text

UI TOOLKITS Layout + Rendering + Input Events

Slide 6

Slide 6 text

TICK TOCK VSYNC sets the pace.

Slide 7

Slide 7 text

CHOREOGRAPHER f1 f2 f3 f4 f5 . . . . . . VSYNC (60fps) Resize view Redraw view Input Events

Slide 8

Slide 8 text

CHOREOGRAPHER public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { ... scheduleVsync(); ... } ... void doFrame(long frameTimeNanos, int frame) { ... if (!mFrameScheduled) { return; } ... doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); ... } Choreographer.java

Slide 9

Slide 9 text

ViewRootImpl Connects WindowManager with the View framework

Slide 10

Slide 10 text

ViewRootImpl *

Slide 11

Slide 11 text

FRAMEWORK Measure + Layout + Draw

Slide 12

Slide 12 text

TRAVERSAL * performTraversals() f2 performTraversals()

Slide 13

Slide 13 text

MEASURE * measure(int, int) f2 M M M M M M → onMeasure(int, int) measureHierarchy(...)

Slide 14

Slide 14 text

LAYOUT * f2 M L M L M L M L M L M L layout(int, int, int, int) → onLayout(boolean, int, int, int, int) performLayout()

Slide 15

Slide 15 text

DRAW * f2 M L D M L D M L D M L D M L D M L D draw(Canvas) → onDraw(Canvas) performDraw()

Slide 16

Slide 16 text

CHANGES Resize, redraw & animate.

Slide 17

Slide 17 text

* requestLayout() f1 f2 f3 f4 f5 . . . . . .

Slide 18

Slide 18 text

requestLayout() public void requestLayout() { ... if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } ... } void scheduleTraversals() { ... mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } View.java ViewRootImpl.java

Slide 19

Slide 19 text

* Invalidate(...) f1 f2 f3 f4 f5 . . . . . .

Slide 20

Slide 20 text

invalidate(...) public void invalidateInternal(...) { ... mPrivateFlags |= PFLAG_DIRTY; ... if (mParent != null && mAttachInfo != null && l < r && t < b) { final Rect damage = mAttachInfo.mTmpInvalRect; damage.set(l, t, r, b); mParent.invalidateChild(this, damage); } ... } View.java

Slide 21

Slide 21 text

* postOnAnimation() f1 f2 f3 f4 f5 . . . . . . ValueAnimator ...

Slide 22

Slide 22 text

postOnAnimation() public void postOnAnimation(Runnable action) { ... attachInfo.mViewRootImpl.mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, action, null); ... } void doFrame(long frameTimeNanos, int frame) { ... doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); ... } View.java Choreographer.java

Slide 23

Slide 23 text

LAZY MEASURE Multi-MeasureSpec cache, invalidated in requestLayout()

Slide 24

Slide 24 text

LAZY MEASURE public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... int cacheIndex = mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ... } View.java

Slide 25

Slide 25 text

LAZY MEASURE public void layout(int l, int t, int r, int b) { if ((flags & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ... } View.java

Slide 26

Slide 26 text

BASIC TIPS 1. No layout requests during layout 2. No layout requests during animations 3. Invalidate regions when possible

Slide 27

Slide 27 text

PERFORMANCE 1. Simplify your view hierarchy 2. Avoid multi-pass measurement

Slide 28

Slide 28 text

GO CUSTOM Simplify view hierarchy, performance, missing features. http://lucasr.org/?p=3920

Slide 29

Slide 29 text

TREE OBSERVER Use OnPreDrawListener!

Slide 30

Slide 30 text

TRANSITIONS http://lucasr.org/?p=3902

Slide 31

Slide 31 text

OnPreDrawListener // 1. Save layout state and wait for next frame. getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); // 2. Restore original layout state. // 3. Trigger animators towards new layout state. } }

Slide 32

Slide 32 text

PROBE IT! Intercept view methods. Dissect layout traversals. https://github.com/lucasr/probe

Slide 33

Slide 33 text

OVERRIDE VIEW METHODS public class DrawGreen extends Interceptor { private final Paint mPaint; public DrawGreen() { mPaint = new Paint(); mPaint.setColor(Color.GREEN); } @Override public void onDraw(View view, Canvas canvas) { canvas.drawPaint(mPaint); } }

Slide 34

Slide 34 text

DEPLOY public final class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { Probe.deploy(this, new DrawGreen(), new Filter.ViewId(R.id.view2)); super.onCreate(savedInstanceState); setContentView(R.id.main_activity); } }

Slide 35

Slide 35 text

WRAP VIEW METHODS public class LogRequestLayout extends Interceptor { @Override public void requestLayout(View view) { super.requestLayout(view); Log.d(LOGTAG, “requestLayout() on ” + view); } }

Slide 36

Slide 36 text

QUESTIONS? +LucasRocha | @lucasratmundo