Optymalizacja hierarchii widoków na platformie Android

Post on 12-Jan-2017

538 views 1 download

Transcript of Optymalizacja hierarchii widoków na platformie Android

Optymalizacja hierarchii widoków na

platformie Android

Michał Włodarczyk, Mobilization 2015, Łódź

Jak tworzyć wydajne aplikacje?

“Layout Traversals on Android”

Lucas RochaFacebook Inc.

DroidCon 2015, Berlin

Pozycjonowanie

Rysowanie

Interakcje ze zdarzeniami

Czym jest UI dla programisty?

Początki Android’a były trudne ...

Przed Jelly Bean

ViewRoot.java

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

} }

Odświeżanie ekranu w przewidywalnych odstępach czasu

Od Jelly Bean - Choreographer

... f1 f2 f3 f4 f5 ...

Resize view

Redrawview

InputEvents

Choreographer.java

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

ViewRootlmpl

Łączy zarządcę okien z hierarchią widoków

SurfaceFlinger

ViewRootImpl

Hierarchia widoków

Metody związane z widokami

Measure + Layout + Draw

measureHierarchy(...)

measure(int, int) → onMeasure(int, int)

M M M

M M

M

*

Measure

Lazy Measure

Multi-MeasureSpec cache

View.java

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;

} ...

}

Lazy Measure

View.java

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;

} ...

}

Lazy Measure

RelativeLayout

LinearLayout Button

LinearLayout TextView

RelativeLayout RelativeLayout

TextView TextViewTextView TextView TextView TextView

requestLayout()

Multi-MeasureSpace cache

18 19

6.5 ms 0.7 ms

72 14

SDK

czas mierzenia

ilość wywołań

Multi-MeasureSpace cache

layout(int, int, int, int) → onLayout(boolean, int, int, int, int)

M L M L M L

M L M L

M L

*

Layout

performLayout()

performDraw()

draw(Canvas) → onDraw(Canvas)M L D

*

M L D M L DM L D

M L D M L DDraw

View.java

private DisplayList getDisplayList(...) { ... final HardwareCanvas canvas = displayList.start(width, height); ... draw(canvas); ... displayList.end(); ...

}

Display List

Kiedy wykonywany jest layout(), to na pewno zrobiony jest measure()

Kiedy wykonywany jest draw(), to na pewno zrobiony jest layout()

Pamiętaj!

1. Wywołanie getMeasured*() poza onLayout()

2. Alokacja:● onLayout(): Akceptowalna● onMeasure(): Unikać● onDraw(): Nigdy

Unikaj!

Zmiany rozmiaru elementów, odświeżenie stanu, animacje

... f1 f2 f3 f4 f5 ...

Metoda requestLayout()

*

requestLayout()

View.java

public void requestLayout() { ... if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } ...

}

ViewRootImpl.java

void scheduleTraversals() { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ...

}

Metoda requestLayout()

Metoda invalidate()

... f1 f2 f3 f4 f5 ...

*

invalidate(...)

Metoda invalidate()

View.java

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

}

Metoda postOnAnimation()

...... f1 f2 f3 f4 f5 ...

*postOnAmination()valueAnimator...

Metoda postOnAnimation()

View.java

public void postOnAnimation(Runnable action) { ... attachInfo.mViewRootImpl.mChoreographer.postCallback(

Choreographer.CALLBACK_ANIMATION, action, null);...

}

Choreographer.java

void doFrame(long frameTimeNanos, int frame) { ... doCallbacks(Choreographer.CALLBACK_INPUT,frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION,frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL,frameTimeNanos); ...

}

Używajmy OnPreDrawListener!

Tree Observer

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.

} }

Nie wywołuj layout() podczas layout

Nie wywołuj layout() podczas animacji

Wywołuj invalidate() tylko na tych elementach, które chcesz przerysować

Pamiętaj!

1. Prosta hierarchia widoków2. Multi pass layout - unikajmy

Wydajność

Własne widoki

1. Composite Views2. Custom Composite Views3. Flat Custom Views

Własne widoki

Przydatne narzędzia

Hierarchy Viewer

źródło: http://cfile10.uf.tistory.com/image/110412354E5A61652D1F2C

Stereovision Image Calculator

Stereovision Image Calculator

1. Menu zmienione z RelativeLayout na FrameLayout

2. Uproszczenie hierarchii widoków3. Wykres jako Custom Flat View4. Kolejkowanie odświeżania

Stereovision Image Calculator Optymalizacja

Nexus 7

Nexus 7 po optymalizacja

Samsung Tab 2

Samsung tab 2 po optymalizacja

Stereovision Image CalculatorWyniki optymalizacji

299.03

50.54

478.42

65.92

Czas renderowania [ms]

Po co tworzyć wydajne aplikacje?

INFO@OPEN-RND.PL

QUESTIONS ? THOUGHTS ? COMMENTS ?

Feel free to contact us!

WWW.OPEN-RND.PL

Thank you

Michał WłodarczykSENIOR SOFTWARE DEVELOPER of Open-RnD

michal.wlodarczyk@open-rnd.pl