Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
323
On Slideshare
323
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
6
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Turbo-charge your UI Romain Guy May 29, 2009
  • 2. Disclaimer
  • 3. 4 Agenda • Adapters • Backgrounds and images • Drawing and invalidating • Views and layouts • Memory allocations
  • 4. 5 Agenda • Adapters • Backgrounds and images • Drawing and invalidating • Views and layouts • Memory allocations
  • 5. 6 Adapters • Awesome • Painful • Do you even know how ListView works?
  • 6. 7 Man in the middle
  • 7. 8 Gimme views • For each position – Adapter.getView() • A new View is returned – Expensive • What if I have 1,000,000 items?
  • 8. 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 1 Item 8
  • 9. 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; }
  • 10. 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; }
  • 11. 12 Even better static class ViewHolder { TextView text; ImageView icon; }
  • 12. 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 }
  • 13. 14 Is it worth it? 0 10 20 30 40 Dumb Recycling views ViewHolder Frames per second
  • 14. 15 Agenda • Adapters • Backgrounds and images • Drawing and invalidating • Views and layouts • Memory allocations
  • 15. 16 Don’t be greedy • Background drawables always fit the view – Stretching may occur • Runtime scaling is expensive
  • 16. 17 How expensive? 0 12.5 25 37.5 50 Auto-scaled Pre-scaled Frames per second
  • 17. 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
  • 18. 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
  • 19. Removing the background 20 <!-- res/values/styles.xml --> <resources> <style name="Theme.NoBackground" parent="android:Theme"> <item name="android:windowBackground">@null</item> </style> </resources>
  • 20. Removing the background 21 <activity android:name="MyApplication" android:theme="@style/NoBackgroundTheme"> <!-- intent filters and stuff --> </activity>
  • 21. 22 What do I get? 0 14 28 42 56 With background No background Frames per second
  • 22. 23 Good news!
  • 23. 24 Agenda • Adapters • Backgrounds and images • Drawing and invalidating • Views and layouts • Memory allocations
  • 24. Redraw efficiently • invalidate() – So easy – So expensive • Dirty regions – invalidate(Rect) – invalidate(left, top, right, bottom) 25
  • 25. Demo, invalidating Home
  • 26. 27 Just do it 0 10 20 30 40 50 invalidate() invalidate(Rect) invalidate(int, int, int, int) Frames per second
  • 27. 28 Agenda • Adapters • Backgrounds and images • Drawing and invalidating • Views and layouts • Memory allocations
  • 28. 29 Fewer is better • Many views – Longer startup time – Slower measurement – Slower layout – Slower drawing • Deep hierarchies – StackOverflowException – Slow... slow... slow...
  • 29. HierarchyViewer
  • 30. 31 A few solutions • TextView’s compound drawables • ViewStub • <merge /> • RelativeLayout • Custom views • Custom layouts
  • 31. 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" />
  • 32. 33 ViewStub
  • 33. 34 ViewStub
  • 34. 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" />
  • 35. 36 Inflating a ViewStub findViewById(R.id.stub_import).setVisibility(View.VISIBLE); // or View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
  • 36. 37 <merge />
  • 37. 38 <merge /> <!-- The merge tag must be the root tag --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Content --> </merge>
  • 38. 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)
  • 39. 40 Custom views
  • 40. 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); } }
  • 41. 42 Custom layouts
  • 42. 43 Custom layouts public 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); } } } }
  • 43. 44 Agenda • Adapters • Backgrounds and images • Drawing and invalidating • Views and layouts • Memory allocations
  • 44. 45 DO NOT ALLOCATE MEMORY • As much as you can • GC – Stops the world – Slow (~x00 ms)
  • 45. 46 Performance sensitive paths Measurement onMeasure() Layout onLayout() Drawing draw() dispatchDraw() onDraw() Events handling dispatchTouchEvent() onTouchEvent() Adapters getView() bindView()
  • 46. 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); }
  • 47. Demo, allocation tracker
  • 48. 49 Manage your objects • SoftReferences – Excellent for memory caches • WeakReferences – Avoids memory leaks
  • 49. 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; }
  • 50. 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
  • 51. Q&A