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

Optimizing UI Performance in Android Apps

Optimizing UI Performance in Android Apps

Sriram Ramani

November 29, 2013
Tweet

More Decks by Sriram Ramani

Other Decks in Programming

Transcript

  1. Optimizing UI
    Performance in
    Android Apps
    Sriram Ramasubramanian
    @sriramramani

    View Slide

  2. “a lot snappier now”
    Firefox for Android
    “starts up quicker”
    “best overall browser
    experience”
    “tablet version was
    "Buttery" Smooth!”

    View Slide

  3. Photo application
    Card UI
    ListView of photos
    Clickable areas in each
    list item
    Shutter

    View Slide

  4. View Slide

  5. Tools

    View Slide

  6. Number of times a pixel on
    the screen is touched
    1x Blue
    2x Green
    3x Red
    4+ Deep Red
    Show GPU Overdraw

    View Slide

  7. An aggregated counter for
    overall overdraw
    ~2x is good
    Debug GPU Overdraw

    View Slide

  8. Time taken to draw each
    frame
    Green line is 16ms mark
    Blue update lists
    Red process lists
    Yellow swap buffers
    Profile GPU Rendering

    View Slide

  9. Time taken to draw each
    frame
    Green line is 16ms mark
    Blue update lists
    Red process lists
    Yellow swap buffers
    Profile GPU Rendering

    View Slide

  10. Profile GPU Rendering
    Hey bro!
    Why so slow?

    View Slide

  11. Systrace.py

    View Slide

  12. Droid Inspector
    http://sriramramani.com/droidinspector

    View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. import com.sriramramani.droid.inspector.server.ViewServer;
    !
    public class MyActivity extends Activity {
    !
    @Override
    public void onCreate(Bundle ofJoy) {
    super.onCreate(ofJoy);
    ViewServer.get(this).addWindow(this);
    }
    !
    @Override
    public void onDestroy() {
    super.onDestroy();
    ViewServer.get(this).removeWindow(this);
    }
    !
    @Override
    public void onResume() {
    super.onResume();
    ViewServer.get(this).setFocusedWindow(this);
    }
    }

    View Slide

  19. Techniques

    View Slide

  20. Remove Overdraw
    1

    View Slide

  21. View Slide

  22. View Slide

  23. Redundant Backgrounds

    View Slide


  24. <br/><item name=“windowBackground"><br/>@android:drawable/screen_background_selector_light<br/></item><br/>
    !
    !
    !
    !
    !
    !
    // Solution
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
    !
    super.onWindowFocusChanged(hasFocus);
    !
    // Reset the background
    getWindow().setBackgroundDrawable(null);
    }

    View Slide

  25. Redundant Backgrounds

    View Slide

  26. Redundant Backgrounds

    View Slide

  27. Redundant Backgrounds

    View Slide

  28. Redundant Backgrounds

    View Slide

  29. Reduce Views
    2

    View Slide

  30. PhotoItemView

    View Slide

  31. Compound Drawables
    Merging an ImageView (icon)
    with the TextView (label)
    And the drawables support
    states!
    Accompanying Label

    View Slide

  32. Compound Drawables
    Merging an ImageView (icon)
    with the TextView (label)
    And the drawables support
    states!
    Accompanying Label

    View Slide

  33. Compound Drawables

    View Slide

  34. Compound Drawables

    View Slide

  35. Compound Drawables

    View Slide

  36. android:layout_height=“match_parent”>
    !
    android:layout_height=“24dp”
    android:src=“@drawable/icon”/>
    !
    android:layout_height=“match_parent”
    android:paddingLeft=“10dp”
    android:text=“Label”/>
    !

    View Slide

  37. android:layout_height=“match_parent”
    android:drawableLeft=“@drawable/icon”
    android:drawablePadding=“10dp”
    android:text=“Label”/>
    !
    !
    !
    !
    textView.setCompoundDrawables(left, top, right, bottom);
    !
    textView.setCompoundDrawablesWithIntrinsicBounds(left,
    top,
    right,
    bottom);
    !
    textView.setCompoundDrawablesRelative(start, top, end, bottom);

    View Slide

  38. Similar to element
    in HTML
    ForegroundColorSpan

    BackgroundColorSpan

    TextAppearanceSpan

    ImageSpan

    UnderlineSpan

    URLSpan
    Spannable String

    View Slide

  39. SpannableStringBuilder builder = new SpannableStringBuilder(_text_);
    !
    ForegroundColorSpan foreground = new ForegroundColorSpan(0xFFFF0000);
    !
    builder.setSpan(foreground,
    startIndex,
    endIndex,
    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
    !
    mTextView.setText(builder, BufferType.SPANNABLE);

    View Slide

  40. Spannable String

    View Slide

  41. Spannable String

    View Slide

  42. Spannable String

    View Slide

  43. Manipulate the
    Canvas 3

    View Slide

  44. Redundant Backgrounds

    View Slide

  45. Redundant Backgrounds

    View Slide

  46. Porter-Duff
    Alpha compositing
    Combining multiple images into a
    single, final image
    Determines how to apply a SRC
    on a DST

    View Slide

  47. Porter-Duff
    SRC_OVER

    View Slide

  48. Porter-Duff
    DST_OVER

    View Slide

  49. Porter-Duff
    DST_IN

    View Slide

  50. Porter-Duff
    DST_OUT

    View Slide

  51. Porter-Duff
    DST SRC DST_IN

    View Slide

  52. Porter-Duff
    DST SRC DST_IN

    View Slide

  53. @Override
    public void draw(Canvas canvas) {
    canvas.save();
    !
    // Ask parent to draw. This is the DST.
    super.draw(canvas);
    !
    // Create an off-screen bitmap.
    Bitmap mask = Bitmap.createBitmap(width,
    height,
    Bitmap.Config.ARGB_8888);
    // Draw the path on the bitmap. This is the SRC.
    (new Canvas(mask)).drawPath(mPath, mPaint);
    // Draw the bitmap containing the path, over the actual canvas.
    mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
    canvas.drawBitmap(mask, 0, 0, mPaint);
    // Reset paint, as it will be used again to draw a path on a bitmap.
    mPaint.setXfermode(null);
    !
    canvas.restore();
    }

    View Slide

  54. Porter-Duff

    View Slide

  55. Porter-Duff

    View Slide

  56. Shaders
    Horizontal spans of colors during
    drawing
    Added to the Paint of the Canvas
    Analogous to a paint brush in
    Photoshop
    Paint with a color is the simplest
    shader

    View Slide

  57. Shaders
    Bitmap Shader Path Result

    View Slide

  58. public void setImage(Bitmap bitmap) {
    BitmapShader bitmapShader = new BitmapShader(mBitmap,
    Shader.TileMode.CLAMP,
    Shader.TileMode.CLAMP);
    mPaint.setShader(bitmapShader);
    }
    !
    !
    !
    !
    !
    @Override
    public void draw(Canvas canvas) {
    // Draw the bitmap.
    canvas.drawPath(mPath, mPaint);
    }

    View Slide

  59. Shaders
    Shader A Shader B
    Porter-Duff
    Compose Shader

    View Slide

  60. Shaders
    Bitmap Shader Linear Gradient
    DST_IN
    Porter-Duff

    View Slide

  61. public void setImage(Bitmap bitmap) {
    BitmapShader bitmapShader = new BitmapShader(mBitmap,
    Shader.TileMode.CLAMP,
    Shader.TileMode.CLAMP);
    !
    LinearGradient gradient = new LinearGradient(0, 0,
    0, bitmap.getHeight(),
    0xFF000000, 0x0,
    Shader.TileMode.CLAMP);
    // bitmap is the DST. gradient is the SRC.
    mPaint.setShader(new ComposeShader(bitmapShader,
    gradient,
    PorterDuff.Mode.DST_IN));
    }
    !
    !
    @Override
    public void draw(Canvas canvas) {
    // Draw the bitmap.
    canvas.drawPaint(mPaint);
    }

    View Slide

  62. Shaders

    View Slide

  63. Custom Views
    Simple layouts can draw
    themselves
    Override onMeasure() and
    onDraw()

    View Slide

  64. @Override
    protected void onMeasure(Canvas canvas) {
    // Ask the parent to set the padding.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    !
    int children = (mContacts == null) ? 0 : mContacts.length;
    !
    int eachChildWidth = mChildSize + (2 * mChildMargin);
    !
    int width = getPaddingLeft() + getPaddingRight() +
    (children * eachChildWidth);
    !
    setMeasuredDimension(width, getMeasuredHeight());
    }

    View Slide

  65. @Override
    protected void onDraw(Canvas canvas) {
    canvas.save();
    !
    // Translate the canvas.
    canvas.translate(getPaddingLeft(), ((getHeight() - mChildSize)/2));
    !
    final int length = mShaders.length;
    !
    for (int i = 0; i < length; i++) {
    canvas.translate(mChildMargin, 0);
    !
    // Set the child shader.
    mPaint.setShader(mShaders[i]);
    !
    canvas.drawRoundRect(mChildBounds,
    mChildCorner,
    mChildCorner,
    mPaint);
    !
    canvas.translate(mChildSize + mChildMargin, 0);
    }
    !
    canvas.restore();
    }

    View Slide

  66. Compare Results

    View Slide

  67. View Slide

  68. View Slide

  69. PhotoItemView

    View Slide

  70. 3.86x 1.09x

    View Slide

  71. before after

    View Slide

  72. before after
    before

    View Slide

  73. Systrace.py

    View Slide

  74. That’s all folks!
    !
    http://sriramramani.wordpress.com
    http://sriramramani.com/droidinspector
    http://github.com/sriramramani/Shutter

    View Slide