Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Layout Traversals (GDG Devfest 2014)

1f461eee0b22011d0bccf4e882d9149f?s=47 Lucas Rocha
November 15, 2014

Layout Traversals (GDG Devfest 2014)

Presented at GDG Devfest London 2014

1f461eee0b22011d0bccf4e882d9149f?s=128

Lucas Rocha

November 15, 2014
Tweet

Transcript

  1. LAYOUT TRAVERSALS Devfest London, 2014

  2. LUCAS ROCHA +LucasRocha | @lucasratmundo

  3. None
  4. BE DELIBERATE Less handwaviness in your UI code

  5. UI TOOLKITS Layout + Rendering + Input Events

  6. TICK TOCK VSYNC sets the pace.

  7. CHOREOGRAPHER f1 f2 f3 f4 f5 . . . .

    . . VSYNC (60fps) Resize view Redraw view Input Events
  8. 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
  9. ViewRootImpl Connects WindowManager with the View framework

  10. ViewRootImpl *

  11. FRAMEWORK Measure + Layout + Draw

  12. TRAVERSAL * performTraversals() f2 performTraversals()

  13. MEASURE * measure(int, int) f2 M M M M M

    M → onMeasure(int, int) measureHierarchy(...)
  14. 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()
  15. 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()
  16. CHANGES Resize, redraw & animate.

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

    . . .
  18. 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
  19. * Invalidate(...) f1 f2 f3 f4 f5 . . .

    . . .
  20. 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
  21. * postOnAnimation() f1 f2 f3 f4 f5 . . .

    . . . ValueAnimator ...
  22. 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
  23. LAZY MEASURE Multi-MeasureSpec cache, invalidated in requestLayout()

  24. 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
  25. 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
  26. BASIC TIPS 1. No layout requests during layout 2. No

    layout requests during animations 3. Invalidate regions when possible
  27. PERFORMANCE 1. Simplify your view hierarchy 2. Avoid multi-pass measurement

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

  29. TREE OBSERVER Use OnPreDrawListener!

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

  31. 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. } }
  32. PROBE IT! Intercept view methods. Dissect layout traversals. https://github.com/lucasr/probe

  33. 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); } }
  34. 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); } }
  35. WRAP VIEW METHODS public class LogRequestLayout extends Interceptor { @Override

    public void requestLayout(View view) { super.requestLayout(view); Log.d(LOGTAG, “requestLayout() on ” + view); } }
  36. QUESTIONS? +LucasRocha | @lucasratmundo