$30 off During Our Annual Pro Sale. View Details »

Layout Traversals (GDG Devfest 2014)

Lucas Rocha
November 15, 2014

Layout Traversals (GDG Devfest 2014)

Presented at GDG Devfest London 2014

Lucas Rocha

November 15, 2014
Tweet

More Decks by Lucas Rocha

Other Decks in Technology

Transcript

  1. LAYOUT
    TRAVERSALS
    Devfest London, 2014

    View Slide

  2. LUCAS ROCHA
    +LucasRocha | @lucasratmundo

    View Slide

  3. View Slide

  4. BE DELIBERATE
    Less handwaviness in your UI code

    View Slide

  5. UI TOOLKITS
    Layout + Rendering + Input Events

    View Slide

  6. TICK TOCK
    VSYNC sets the pace.

    View Slide

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

    View Slide

  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

    View Slide

  9. ViewRootImpl
    Connects WindowManager with the
    View framework

    View Slide

  10. ViewRootImpl
    *

    View Slide

  11. FRAMEWORK
    Measure + Layout + Draw

    View Slide

  12. TRAVERSAL
    * performTraversals()
    f2
    performTraversals()

    View Slide

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

    View Slide

  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()

    View Slide

  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()

    View Slide

  16. CHANGES
    Resize, redraw & animate.

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  29. TREE OBSERVER
    Use OnPreDrawListener!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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);
    }
    }

    View Slide

  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);
    }
    }

    View Slide

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

    View Slide

  36. QUESTIONS?
    +LucasRocha | @lucasratmundo

    View Slide