Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Layout Traversals (Droidcon Turin 2015)

Lucas Rocha
April 10, 2015
590

Layout Traversals (Droidcon Turin 2015)

Presented at Droidcon Turin 2015

Lucas Rocha

April 10, 2015
Tweet

Transcript

  1. 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
  2. CHOREOGRAPHER f1 f2 f3 f4 f5 . . . .

    . . VSYNC (60fps) Resize view Redraw view Input Events
  3. 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
  4. MEASURE * measure(int, int) f2 M M M M M

    M → onMeasure(int, int) measureHierarchy(...)
  5. 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
  6. 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
  7. 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()
  8. 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
  9. 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()
  10. DISPLAY LISTS private DisplayList getDisplayList(...) { ... final HardwareCanvas canvas

    = displayList.start(width, height); ... draw(canvas); ... displayList.end(); ... } View.java
  11. 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”
  12. 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()); } }); } ... }
  13. 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. } }
  14. requestLayout() public void requestLayout() { ... if (mParent != null

    && !mParent.isLayoutRequested()) { mParent.requestLayout(); } ... } void scheduleTraversals() { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } View.java ViewRootImpl.java
  15. 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
  16. 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
  17. BASIC TIPS 1. No layout requests during layout 2. No

    layout requests during animations 3. Invalidate regions when possible
  18. WRAP VIEW METHODS public class LogRequestLayout extends Interceptor { @Override

    public void requestLayout(View view) { super.requestLayout(view); Log.d(LOGTAG, “requestLayout() on ” + view); } }
  19. 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); } }