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

Layout Traversals (Droidcon Turin 2015)

1f461eee0b22011d0bccf4e882d9149f?s=47 Lucas Rocha
April 10, 2015
570

Layout Traversals (Droidcon Turin 2015)

Presented at Droidcon Turin 2015

1f461eee0b22011d0bccf4e882d9149f?s=128

Lucas Rocha

April 10, 2015
Tweet

Transcript

  1. LAYOUT TRAVERSALS Droidcon Turin, 2015

  2. LUCAS ROCHA +LucasRocha | @lucasratmundo

  3. None
  4. BE DELIBERATE Less handwavy UI code

  5. THE BASICS From inside out.

  6. UI TOOLKITS Layout + Rendering + Input Events

  7. OLD SCHOOL Nested boxes, rudimentary motion API.

  8. PRE-JELLY BEAN public final class ViewRoot extends Handler ... {

    ... public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; sendEmptyMessage(DO_TRAVERSAL); } } ... public void handleMessage(Message msg) { ... case DO_TRAVERSAL: performTraversals(); ... } } ViewRoot.java
  9. MODERNIZE Predictable notion of time that drives input, layout, and

    motion.
  10. TICK TOCK VSYNC sets the pace. No tearing, no extra

    work.
  11. CHOREOGRAPHER f1 f2 f3 f4 f5 . . . .

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

  14. THE ROOT * * SurfaceFlinger ViewRootImpl

  15. VIEWS Measure + Layout + Draw

  16. TRAVERSAL * performTraversals() f2 performTraversals()

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

    M → onMeasure(int, int) measureHierarchy(...)
  18. LAZY MEASURE Multi-MeasureSpec cache, invalidated in requestLayout()

  19. 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
  20. 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
  21. 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()
  22. FRAME IT public void layout(int l, int t, int r,

    int b) { ... boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); ... } View.java
  23. 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()
  24. DISPLAY LISTS private DisplayList getDisplayList(...) { ... final HardwareCanvas canvas

    = displayList.start(width, height); ... draw(canvas); ... displayList.end(); ... } View.java
  25. INVARIANTS Layout means measurement is done. Drawing means layout is

    done.
  26. SMELLS 1. getMeasured*() calls outside onLayout() 2. New allocations during

    traversal · onLayout: ok-ish · onMeasure: avoid · onDraw: nope 3. post(Runnable) to mean “after layout”
  27. HAPPENS TO WORK public final class MyActivity extends Activity {

    ... @Override public void onCreate() { final View myView = findViewById(R.id.some_id); myView.post(new Runnable() { @Override public void run() { Log.d(“LOGTAG”, myView.getWidth()); } }); } ... }
  28. LOOPER BARRIERS void scheduleTraversals() { ... mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); ...

    } ViewRootImpl.java
  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. CHANGES Resize, redraw & animate.

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

    . . .
  34. requestLayout() public void requestLayout() { ... if (mParent != null

    && !mParent.isLayoutRequested()) { mParent.requestLayout(); } ... } void scheduleTraversals() { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } View.java ViewRootImpl.java
  35. * Invalidate(...) f1 f2 f3 f4 f5 . . .

    . . .
  36. invalidate(...) public void invalidateInternal(...) { ... mPrivateFlags |= PFLAG_INVALIDATED; ...

    if (mParent != null && mAttachInfo != null && l < r && t < b) { final Rect damage = mAttachInfo.mTmpInvalRect; damage.set(l, t, r, b); mParent.invalidateChild(this, damage); } ... } boolean draw(...) { ... mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) == PFLAG_INVALIDATED; ... } View.java
  37. * postOnAnimation() f1 f2 f3 f4 f5 . . .

    . . . ValueAnimator ...
  38. 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
  39. BASIC TIPS 1. No layout requests during layout 2. No

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

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

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

  43. WRAP VIEW METHODS public class LogRequestLayout extends Interceptor { @Override

    public void requestLayout(View view) { super.requestLayout(view); Log.d(LOGTAG, “requestLayout() on ” + view); } }
  44. 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); } }
  45. FANCY HACKING ON DEEP LAYOUT STUFF? We're hiring :-)

  46. QUESTIONS? +LucasRocha | @lucasratmundo