Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

53

Transcript of Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Page 1: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Page 2: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Turbo-charge your UIRomain GuyMay 29, 2009

Page 3: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Disclaimer

Page 4: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

4

Agenda

• Adapters• Backgrounds and images• Drawing and invalidating• Views and layouts• Memory allocations

Page 5: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

5

Agenda

• Adapters• Backgrounds and images• Drawing and invalidating• Views and layouts• Memory allocations

Page 6: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

6

Adapters

• Awesome• Painful• Do you even know how ListView works?

Page 7: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

7

Man in the middle

Page 8: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

8

Gimme views

• For each position– Adapter.getView()

• A new View is returned– Expensive

• What if I have 1,000,000 items?

Page 9: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Adapter

9

Getting intimate with ListView

Item 2

Item 3

Item 4

Item 5

Item 6

Item 7

Recycler

Type 1

Type 2

Type n

Item 1Item 8

Page 10: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

10

Don’t

public View getView(int position, View convertView, ViewGroup parent) { View item = mInflater.inflate(R.layout.list_item_icon_text, null);

((TextView) item.findViewById(R.id.text)).setText(DATA[position]); ((ImageView) item.findViewById(R.id.icon)).setImageBitmap( (position & 1) == 1 ? mIcon1 : mIcon2);

return item;}

Page 11: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

11

Do

public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.item, null); }

((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]); ((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap( (position & 1) == 1 ? mIcon1 : mIcon2);

return convertView;}

Page 12: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

12

Even better

static class ViewHolder { TextView text; ImageView icon; }

Page 13: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

13

Even better

1 public View getView(int position, View convertView, ViewGroup parent) { 2 ViewHolder holder; 3 4 if (convertView == null) { 5 convertView = mInflater.inflate(R.layout.list_item_icon_text, null); 6 7 holder = new ViewHolder(); 8 holder.text = (TextView) convertView.findViewById(R.id.text); 9 holder.icon = (ImageView) convertView.findViewById(R.id.icon); 10 11 convertView.setTag(holder); 12 } else { 13 holder = (ViewHolder) convertView.getTag(); 14 } 15 16 holder.text.setText(DATA[position]); 17 holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2); 18 19 return convertView; 20 }

Page 14: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

14

Is it worth it?

0

10

20

30

40

Dumb Recycling views ViewHolder

Frames per second

Page 15: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

15

Agenda

• Adapters• Backgrounds and images• Drawing and invalidating• Views and layouts• Memory allocations

Page 16: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

16

Don’t be greedy

• Background drawables always fit the view– Stretching may occur

• Runtime scaling is expensive

Page 17: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

17

How expensive?

0

12.5

25

37.5

50

Auto-scaled Pre-scaled

Frames per second

Page 18: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Pre-scaling is easy

18

// Rescales originalImage to the size of view using// bitmap filtering for better results

originalImage = Bitmap.createScaledBitmap( originalImage, // bitmap to resize view.getWidth(), // new width view.getHeight(), // new height true); // bilinear filtering

Page 19: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Window backgrounds

• Sometimes unnecessary– Top-level opaque view– layout_width=fill_parent– layout_height=fill_parent

• Expensive to draw• Dumb rendering engine

– (And it’s my fault)

19

Page 20: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Removing the background

20

<!-- res/values/styles.xml --><resources> <style name="Theme.NoBackground" parent="android:Theme"> <item name="android:windowBackground">@null</item> </style></resources>

Page 21: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Removing the background

21

<activity android:name="MyApplication" android:theme="@style/NoBackgroundTheme">

<!-- intent filters and stuff -->

</activity>

Page 22: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

22

What do I get?

0

14

28

42

56

With background No background

Frames per second

Page 23: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

23

Good news!

Page 24: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

24

Agenda

• Adapters• Backgrounds and images• Drawing and invalidating• Views and layouts• Memory allocations

Page 25: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Redraw efficiently

• invalidate()– So easy– So expensive

• Dirty regions– invalidate(Rect)– invalidate(left, top, right, bottom)

25

Page 26: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Demo, invalidating Home

Page 27: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

27

Just do it

0

10

20

30

40

50

invalidate() invalidate(Rect) invalidate(int, int, int, int)

Frames per second

Page 28: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

28

Agenda

• Adapters• Backgrounds and images• Drawing and invalidating• Views and layouts• Memory allocations

Page 29: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

29

Fewer is better

• Many views– Longer startup time– Slower measurement– Slower layout– Slower drawing

• Deep hierarchies– StackOverflowException– Slow... slow... slow...

Page 30: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

HierarchyViewer

Page 31: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

31

A few solutions

• TextView’s compound drawables• ViewStub• <merge />• RelativeLayout• Custom views• Custom layouts

Page 32: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

32

Compound drawables

<LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content">

<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" />

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" />

</LinearLayout>

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello" android:drawableLeft="@drawable/icon" />

Page 33: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

33

ViewStub

Page 34: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

34

ViewStub

Page 35: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

35

ViewStub

<ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/panel_import"

android:layout="@layout/progress_overlay"

android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" />

Page 36: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

36

Inflating a ViewStub

findViewById(R.id.stub_import).setVisibility(View.VISIBLE);

// or

View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

Page 37: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

37

<merge />

Page 38: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

38

<merge />

<!-- The merge tag must be the root tag --><merge xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Content -->

</merge>

Page 39: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

39

RelativeLayout

• Powerful• Replace linear layouts

– A horizontal LinearLayout in a vertical one– Or the other way around

• Sometimes hard to use– (And it’s all my fault)

Page 40: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

40

Custom views

Page 41: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

41

Custom views

class CustomView extends View { public CustomView(Context context) { super(context); }

@Override protected void onDraw(Canvas canvas) { }

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(100, 100); }}

Page 42: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

42

Custom layouts

Page 43: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

43

Custom layoutspublic class GridLayout extends ViewGroup { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); // Define measurement spec of each child child.measure(childWidthSpec, childheightSpec); }

setMeasuredDimension(widthSpecSize, heightSpecSize); }

@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { // Compute position of each child child.layout(left, top, right, bottom); } } }}

Page 44: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

44

Agenda

• Adapters• Backgrounds and images• Drawing and invalidating• Views and layouts• Memory allocations

Page 45: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

45

DO NOT ALLOCATE MEMORY

• As much as you can• GC

– Stops the world– Slow (~x00 ms)

Page 46: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

46

Performance sensitive paths

Measurement onMeasure()

Layout onLayout()

Drawing draw()

dispatchDraw()

onDraw()

Events handling dispatchTouchEvent()

onTouchEvent()

Adapters getView()

bindView()

Page 47: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

47

Fail fast

int prevLimit = -1;try { // Limit the number of allocated objects prevLimit = Debug.setAllocationLimit(0);

// Execute code that should not perform // any allocation} finally { Debug.setAllocationLimit(prevLimit);}

Page 48: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Demo, allocation tracker

Page 49: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

49

Manage your objects

• SoftReferences– Excellent for memory caches

• WeakReferences– Avoids memory leaks

Page 50: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

50

Simple cache private final HashMap<String, SoftReference<T>> mCache;

public put(String key, T value) { mCache.put(key, new SoftReference<T>(value)); }

public T get(String key, ValueBuilder builder) { T value = null; SoftReference<T> reference = mCache.get(key);

if (reference != null) { value = reference.get(); }

// Not in cache or gc'd if (value == null) { value = builder.build(key); mCache.put(key, new SoftReference<T>(value)); } return value; }

Page 51: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

51

Resources

• http://d.android.com• http://source.android.com• http://android.git.kernel.org• http://code.google.com/p/apps-for-android• http://code.google.com/p/shelves

Page 52: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient

Q&A

Page 53: Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient